From 1ba26e012461ccbbcbd1128d1bbcd9a94240831c Mon Sep 17 00:00:00 2001 From: Terry Thorsen Date: Thu, 29 Feb 2024 12:21:40 -0500 Subject: [PATCH 001/152] First pass browser-resident DAs --- CHANGELOG.md | 7 + docs/api/spec.md | 14 +- docs/api/supported-platforms.md | 98 ++++++++-- docs/specs/browserCommunicationProtocol.md | 5 + docs/specs/browserResidentDesktopAgents.md | 213 +++++++++++++++++++++ docs/specs/getAgent.md | 141 ++++++++++++++ docs/specs/preloadDesktopAgents.md | 41 ++++ src/api/GetAgent.ts | 132 +++++++++++++ 8 files changed, 634 insertions(+), 17 deletions(-) create mode 100644 docs/specs/browserCommunicationProtocol.md create mode 100644 docs/specs/browserResidentDesktopAgents.md create mode 100644 docs/specs/getAgent.md create mode 100644 docs/specs/preloadDesktopAgents.md create mode 100644 src/api/GetAgent.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ded336c5f..4d7a65362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +* Specifications for getAgent() and Browser-Resident Desktop Agents. +* Specification for Preload Desktop Agents. This content was previously in the supported platforms section. It had been revised and amended to include recommended behavior related to the new ValidateAppIdentity() function. +* Typescript definitions for getAgent() and related types +* Typescript definitions for Browser Communication Protocol (BCP). These constitute the internal "wire protocol" that the "@finos/fdc3" library uses to communicate with Browser-Resident DAs. + ### Changed +* FDC3 apps are now encouraged to instantiate their FDC3 interface (DesktopAgent) using the `getAgent()` function provided by the `@finos/fdc3` module. This will allow apps to interoperate in either traditional Preload DAs (i.e. Electron) as well as the new Browser-Resident DAs. + ### Deprecated ### Fixed diff --git a/docs/api/spec.md b/docs/api/spec.md index 569f00ba5..60ba0667a 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -20,8 +20,7 @@ A Desktop Agent is a desktop component (or aggregate of components) that serves Examples of Desktop Agents include: - Autobahn -- Finsemble -- Glue42 +- io.Connect - OpenFin - Refinitiv Eikon @@ -52,6 +51,14 @@ The FDC3 API specification consists of interfaces. It is expected that each Des Other interfaces defined in the spec are not critical to define as concrete types. Rather, the Desktop Agent should expect to have objects of the interface shape passed into or out of their library. +FDC3 supports multiple types of desktop agents: + +- [Preload DAs](../specs/preloadDesktopAgents.md) - DAs that use a technology that allows the `fdc3` interface to be instantiated on the global object without any action by the app itself. Example technologies that support preloading include DAs built with Electron, Webviews, and Browser Extensions. +- [Browser-resident DAs](../specs/browserResidentDesktopAgents) - DAs that run in standard browsers without the assistance of any non-standard technology. +- Native language DAs - DAs that run in native containers. + +Communication between different types of DA may be handled in a proprietary manner by vendors or through [Desktop Agent Bridging](#inter-agent-communication). + For implementation details relating to particular languages, and how to access the API in those languages, please see [Supported Platforms](supported-platforms). ### Standards vs. Implementation @@ -111,6 +118,8 @@ An FDC3 Standard compliant Desktop Agent implementation **MUST**: - Only require app directories that they connect to to have implemented only the minimum requirements specified in the [App Directory API Part](../app-directory/spec) of this Standard. - Provide details of whether they implement optional features of the Desktop Agent API in the `optionalFeatures` property of the [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object returned by the [`fdc3.getInfo()`](ref/DesktopAgent#getinfo) function. - Allow, by default, at least a 15 second timeout for an application, launched via [`fdc3.open`](../api/ref/DesktopAgent#open), [`fdc3.raiseIntent`](../api/ref/DesktopAgent#raiseintent) or [`fdc3.raiseIntentForContext`](../api/ref/DesktopAgent#raiseintentforcontext) to add any context listener (via [`fdc3.addContextListener`](../api/ref/DesktopAgent#addcontextlistener)) or intent listener (via [`fdc3.addIntentListener`](../api/ref/DesktopAgent#addintentlistener)) necessary to deliver context or intent and context to it on launch. This timeout only applies to listeners needed to receive context on launch; further intent and context listeners not required on launch MAY be added later. +- Implement the [Browser-Resident Desktop Agent spec](../specs/browserResidentDesktopAgents.md) if it is intended to run in a standard browser. +- Implement the [Preload Desktop Agent spec](../specs/preloadtDesktopAgents.md) if it is intended to run in a container that supports injecting a global `fdc` object. An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: @@ -122,6 +131,7 @@ An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: - Make metadata about each context message or intent and context message received (including the app that originated the message) available to the receiving application. - Prevent external apps from listening or publishing on a [`PrivateChannel`](ref/PrivateChannel) that they did not request or provide. - Enforce compliance with the expected behavior of intents (where Intents specify a contract that is enforceable by schema, for example, return object types) and return an error if the interface is not met. +- Implement validateAppIdentity() An FDC3 Standard compliant Desktop Agent implementation **MAY**: diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index b5b0454c4..674e1857a 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -9,29 +9,97 @@ There are two main categories of platform: web and native, both of which are des ## Web -For a web application to be FDC3-enabled, it needs to run in the context of an environment or **_Platform Provider_** that makes the FDC3 API available to the application. This environment could be a browser extension, a web or native app, or a fully-fledged desktop container framework. +For a web application to be FDC3-enabled, it needs to run in the context of an environment or **_Platform Provider_** that makes the FDC3 API available to the application (a "Desktop Agent" - DA). Applications use the `DesktopAgent` interface to connect to the DA (we refer to this as the `fdc3` object or the "FDC3 API"). DAs may either be "Preload DAs" (where the DA itself lives in code outside of the browser - such as an Electron app or a Browser Extension) or "Browser Resident DAs" (where the DA lives in a separate frame or window.) -### API Access & Globals +### API Access - Connecting to FDC3 -The FDC3 API can be made available to an application through a number of different methods. In the case of web applications, a Desktop Agent MUST provide the FDC3 API via a global accessible as `window.fdc3`. Implementors MAY additionally make the API available through modules, imports, or other means. +FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc` library and then calling the provided `getAgent()` function. This will ensure that the app can run in any type of DA (either browser-resident DAs or DAs that support injection.) (Apps SHOULD NO LONGER rely on the existence of a global `fdc3` object.) -The global `window.fdc3` must only be available after the API is ready to use. To enable applications to avoid using the API before it is ready, implementors MUST provide a global `fdc3Ready` event that is fired when the API is ready for use. Implementations should first check for the existence of the FDC3 API and add a listener for this event if it is not found: +Apps MUST call `getAgent()` with their identity. This can be done in either of three ways. -```ts -function fdc3Action() { - // Make some fdc3 API calls here -} +1. Provide an appId field -if (window.fdc3) { - fdc3Action(); -} else { - window.addEventListener('fdc3Ready', fdc3Action); -} + The appId SHOULD be _fully qualified_ (containing a domain name). The DA will then use this to construct a query to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/v2/apps/yourApp". + + Example: Obtaining an fdc3 interface + ```JavaScript + import { getAgent } from "@finos/fdc3"; + + try { + const fdc3 = await getAgent({ appId: “yourApp@yourorg.org” }); + } catch (e) { + // Failed to connect + } + ``` + +2. Provide an appDUrl field + + As an alternative to providing a fully qualified appId, apps MAY provide an `appDUrl` field that contains a link to an AppD definition for the app. + + Example: Obtaining an fdc3 interface using an AppD locator + ```JavaScript + try { + const fdc3 = await getAgent({ appDUrl: 'https://yourorg.org/appd/v2/apps/yourApp' }); + } catch (e) { + // console.log(e); // Failed to connect + } + ``` + +3. Provide a both appId and appDUrl fields + + The DA will construct the AppD query according to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/appd/v2/apps/yourApp". + + Example: Obtaining an fdc3 interface using an AppD locator + ```JavaScript + try { + const fdc3 = await getAgent({ appId: "yourApp", appDUrl: 'https://yourorg.org/appd' }); + } catch (e) { + // console.log(e); // Failed to connect + } + ``` + +Applications MAY provide additional fields related to configuration or failover support. See [GetAgentParams](ref/GetAgent) for those options. + +> Note: Applications SHOULD provide visual feedback to users indicating that the app is in the process of connecting. Once the FDC3 interface is accessible the application SHOULD update that visual feedback. + +### Failover function + +Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. + +Example: Decreasing the timeout and providing a failover function +```JavaScript + const fdc3 = await getAgent({ + appId: “myApp@yourorg.org”, + timeout: 250, + failover: async (params) => { + // return WindowProxy | URL | DesktopAgent + } + }); ``` -Since FDC3 is typically available to the whole web application, Desktop Agents are expected to make the [`DesktopAgent`](DesktopAgent) interface available at a global level. +The failover function allows an application to provide a backup mechanism for connecting to a DA. It is called only when establishment through normal procedures fails or times out. + +> Note - Failover can occur quicker than the timeout. For instance when an end user opens an FDC3 app in a new browser tab it will immediately failover because there will be no injected DesktopAgent and there will be no parent or opener windows. + +> Note - A second timeout is started when the failover function is called. So the total possible elapsed time to establish a connection is 2X the established timeout when a failover function is provided. + +> 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. + +Failover functions MUST be asynchronous MUST resolve to one of the following types: + +1) DesktopAgent + +The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. + +2) WindowProxy (Window object) + +The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. + +3) URL + +If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. -The global `window.fdc3` should only be available after the API is ready to use. To prevent the API from being used before it is ready, implementors should provide an `fdc3Ready` event. +If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. ### Usage diff --git a/docs/specs/browserCommunicationProtocol.md b/docs/specs/browserCommunicationProtocol.md new file mode 100644 index 000000000..a5fcb26ff --- /dev/null +++ b/docs/specs/browserCommunicationProtocol.md @@ -0,0 +1,5 @@ +# Browser Communication Protocol (BCP) + +BCP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. + +TODO \ No newline at end of file diff --git a/docs/specs/browserResidentDesktopAgents.md b/docs/specs/browserResidentDesktopAgents.md new file mode 100644 index 000000000..2597c6ecb --- /dev/null +++ b/docs/specs/browserResidentDesktopAgents.md @@ -0,0 +1,213 @@ +# FDC for the Web: Browser-Resident Desktop Agent Specification + +This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by FINOS. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView or Browser Extension based implementations.) + +> Note - Prior to FDC 3.0, only Preload DAs were supported. + +> Note - Along with this specification, a new general connection strategy has been established for _all_ FDC3 compliant applications: FDC3 compliant apps SHOULD make use of the `@finos/fdc3` library to establish their FDC3 interface (a `DesktopAgent` object instance). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or Preload DAs without code modification. We refer to this concept as Write Once Run Anywhere (WORA). + +> Note - This specification only applies to apps running in a browser and therefore assumes use of JavaScript and HTML APIs. Implementations in other languages such as .NET are not covered by this specification. + +This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the `@finos/fdc3` library. Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification](getAgent.md) for information on how the client side operates. + +> Note - When referencing "DA" in this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. + +## Launching apps + +As a prerequisite for running FDC3 in the browser, a DA must first exist as running code in a browser window (See failover functions for an exception to this rule). We will refer to this window as the "DA Window". + +> Note - It is possible to have multiple DA Windows. For instance, a DA may propagate itself into new windows. Communications between DA Windows is an implementation detail. + +When an app runs `getAgent()`, it checks for the existence of `window.parent`, `window.opener` and `window.parent.opener` (collectively called "Parents"). This function sends a "Handshake" message to each Parent via a `postMessage` in the hopes of discovering a DA. + +If we run this logic in reverse we see that apps may be launched: + +(1) By creating an iframe in a DA Window +(2) By calling `window.open` from a DA Window +(3) By creating an iframe in a window that was opened from a DA Window + +> Note - This covers most typical cases but it leaves out more complex launching scenarios such as "daisy chains" (frame -> frame or open -> open) or allowing end users to open tabs in an adhoc manner. To cover these cases, DAs may choose to allow themselves to be opened as hidden iframes within app windows. See the section on hidden iframes at the end of this document. + +## Responding to app instance connections - Web Connection Protocol (WCP) + +DAs MUST run `addEventListener("message",...)` to receive incoming connection requests from apps. This will receive the initial `postMessage` "Handshake" messages from apps. Handshakes do not contain any identifying data. They contain only a "nonce" (a unique id). + +Upon receiving an incoming Handshake the DA MUST: + +1) Create a [MessageChannel](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) +2) Set up a processing thread to receive and process BCP requests for this connection +3) Respond to the event's `source` window by echoing the nonce and transferring `port2` from the channel + +Example WCP negotiation +```JavaScript +window.addEventListener("message", (event) => { + const { origin, data, source } = event + const channel = new MessageChannel() + startBCPProcessing(event, channel) // See next step + source.postMessage({ nonce: data.nonce }, origin, [channel.port2]) +}); +``` + +Alternatively for step 3, a DA MAY return a `url` instead transferring a MessagePort. When `url` is provided in the response, `getAgent()` will open that `url` in a hidden iframe and initiate another WCP handshake with that frame. + +Example WCP negotiation with URL +```JavaScript +window.addEventListener("message", (event) => { + const { origin, data, source } = event + source.postMessage({ nonce: data.nonce, url }, origin) +}); +``` + +## Responding to app communications with Browser Communication Protocol (BCP) + +BCP processing should begin by setting up an inactive connection instance. This instance will become active after the first BCP "ValidateAppIdentity" message is received and processed (or deleted if it fails). It is important to remember the WindowProxy (event.source). + +An `instanceUuid` should be established at this point. This will be used in following steps. + +```JavaScript +const startBCPProcessing(event, channel) => { + const instanceUuid = uuid(); + const connection = { + status: "inactive", + windowProxy: event.source, + origin: event.origin, + channel, + instanceUuid + }; + connections[instanceUuid] = connection; + channel.port1.onmessage = (e) => { + processIncomingBCP(connection, e); // This function should switch based on message type + } +} +``` + +### Step 1 - Validating a connection + +The first BCP message received should be a "ValidateAppIdentity" message. With this message, the DA establishes or denies the connection. + +1) If the message contains an `instanceUuid` field then the app window might have navigated or refreshed and simply require reconnecting on a previously established instance. The DA SHOULD reestablish the connection if the `instanceUuid`, `appId`, WindowProxy and origin match what is already on record. When a connection is reestablished the DA MUST respond with a valid "IdentityValidationSucceeded" message. + +2) If the message does not contain an `instanceUuid`, or the record does not match, then proceed to authenticate the application. + +> Note - Apps that call `window.open()` to create new instances of themselves can appear to be their Parent. This is because browsers may clone SessionStorage for newly opened windows. When a child window calls `getAgent()` with the same appId as the parent window, it will appear to the DA that a navigation event occurred on the parent window (because `instanceUuid` will be set, and will appear to match the appId). DAs therefore MUST track the WindowProxy object that is used to establish each connection and use it as an additional comparison criteria. + +Example BCP validation +```JavaScript +const processValidateAppIdentityBCP = (connection, e) => { + const { data, source, origin } = e; + + if(data.payload?.instanceUuid) { + const maybeExistingConnection = connections[data.payload.instanceUuid]; + // Maybe re-establish an existing connection that was lost due to page navigation + if (data.payload?.appId === maybeExistingConnection.appId && connection.windowProxy === maybeExistingConnection.windowProxy && origin === maybeExistingConnection.origin) { + // Close the ports that are now known to be defunct + maybeExistingConnection.channel.port1.close(); + maybeExistingConnection.channel.port2.close(); + + // Swap out the old and new connection + connections[maybeExistingConnection.instanceUuid] = connection; + delete connections[connection.instanceUuid]; + + // Copy the old into the new. The transfer is now complete. + connection.instanceUuid = maybeExistingConnection.instanceUuid; + connection.instanceId = maybeExistingConnection.instanceId; + connection.appId = maybeExistingConnection.appId; + connection.appMetadata = maybeExistingConnection.appMetadata; + connection.channel.port1.postMessage({ + type: "IdentityValidationSucceeded", + payload: { + desktopAgentDetails: { + agentType: "PARENT", + appId: data.appId, + instanceUUid: data.instanceUuid, + instanceId: connection.instanceId, + }, + appMetaData: connection.appMetaData, + implementationMetadata // The DA's representation of itself as ImplementationMetadata type + } + }, connection.origin); + return; + } + } + authenticateApp(connection, e); // See next step +} +``` + +### Step 2 - Authentication + +An app is authenticated by retrieving the app's AppD record and checking to ensure that the app's origin is contained in either the AppD record's `url` or `allowedOrigins` fields. Apps may provide either a fully qualified `appId` field (containing the domain of the AppD server) or a combination of `appId` and `appDUrl` fields (where `appDUrl` is the url of the AppD server). See [AppD endpoint specification](https://fdc3.finos.org/docs/app-directory/spec) + +**Examples** + +Each of these should result in a request to "https://myapp.com/appd/v2/apps/myApp" +`appId="myApp@myapp.com/appd` +`appId="myApp", appDUrl="https://muyapp.com/appd"` +`appDUrl="https://myapp.com/appd/v2/apps/myApp"` + +The DA MUST validate the app's identity against the application's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the _AppD record constructed from the ValidateAppIdentity message_. + +> Note - The AppD record provided by the app may be different from the one that the DA used to launch the app. This is because a DA may launch apps from its own app directory or even without a directory. However, the responsibility to provide application identity _rests with its publisher_ (the entity that is _hosting_ the app) and hence DA's MUST reference the AppD record that is provided by the application itself for verification. DAs MAY choose to implement _additional_ identity verification procedures (such as comparison with their own configured app directories) and MAY choose to deny access to an application for any reason. + +If authentication succeeds then the DA MUST respond with a "IdentityValidationSucceeded" message which MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to support reconnecting after window navigation). + +```JavaScript +const authenticateApp = (connection, e) => { + const { data : {}, source, origin } = e; + if(source!==connection.windowProxy) { + // Message received from an unexpected window + return; + } + const { appId, appDUrl } = data; + const appDRecord = fetchAppDRecord(appId, appDUrl); // The DA implementor should implement AppD record fetching + if ( originMatch(origin, appDRecord)) { // The DA implementor should build origin matching logic + // The DA may implement any other validation here + // The DA may perform any internal record keeping here + connection.status = "active"; + connection.appId = appId; // Should be the resolved fully qualified appId + connection.instanceId = uuid(); + connection.appMetadata = new AppMetadata(); // To be set by the DA + connection.channel.port1.postMessage({ + type: "IdentityValidationSucceeded", + payload: { + desktopAgentDetails: { + agentType: "PARENT", + appId: data.appId, + instanceUUid: connection.instanceUuid, + instanceId: connection.instanceId, + }, + appMetaData: connection.appMetaData, + implementationMetadata // The DA's representation of itself as ImplementationMetadata type + } + }, connection.origin) + } else { + connection.port.postMessage({ + type: "IdentityValidationFailed", + payload: { + error: `Origin "${origin}" for "${appId}" didn't match AppD record` + } + }, connection.origin) + } +} +``` + +### Step 3 - DesktopAgent Operations (BCP) + +TODO + +See [Browser Communication Protocol](./browserCommunicationProtocol.md) + +## Implementing DAs in hidden iframes + +DA providers can leverage hidden iframes to establish a communication mechanism that is independent of Parent windows. This approach allows apps to connect to a DA even when they were not opened by that DA. + +The hidden iframe url can be provided in two ways: + +1) By a Parent DA - This allows DAs to redirect communications to a DA in a hidden iframe at a known location. The main benefit of this approach is that it can allow a system to continue to operate even if the Parent is closed. + +2) By a failover function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can provide a url. In such a scenario, failover is instantaneous because the absence of Parent windows is deterministic. Note that this approach may compromise Write Once Run Anywhere (WORA) unless apps use dynamic mechanisms to determine the hidden url (such as through clues provided in their href.) + +The same WCP mechanism is used to connect to DAs located in hidden iframes. Likewise, the same BCP messages are used for communications. + + + + diff --git a/docs/specs/getAgent.md b/docs/specs/getAgent.md new file mode 100644 index 000000000..aeaaf2dcf --- /dev/null +++ b/docs/specs/getAgent.md @@ -0,0 +1,141 @@ +# Specification for getAgent() function + +`getAgent()` is implemented in the `@finos/fdc3` library. This specification details how it retrieves and provides FDC3 interface object. + +## Definition of Terms + +**Browser Resident Desktop Agent (DA)**: A Desktop Agent loaded in a window or frame within a web browser (in contrast to a Desktop Agent that "injects" a global fdc3 object). + +> Note - Throughout this document, when referring to "DA" without any other qualification we mean a Browser-Resident Desktop Agent. + +**getAgent()**: The library function provided by `@finos/fdc3` that discovers and establishes communication to DAs. It may (1) return a reference to an injected `DesktopAgent` instance, (2) use the FDC3 Web Connection Protocol (WCP) to discover a DA (e.g. in a "parent" window or frame) and return a `DesktopAgent` instance that communicates with the DA using the FDC3 Browser Communication Protocol (BCP), or (3) run an application provided failover function that provides direct or indirect access to a `DesktopAgent`. + +**Web Connection Protocol (WCP)**: A protocol for discovering and establishing communications with a DA. Ths includes a proscribed algorithm as well as some standard messages that are transmitted using `window.postMessage`. + +**Browser Communication Protocol (BCP)**: A protocol that uses the standard HTML Channel Messaging API (MessagePort) to communicate with a DA in a remote iframe or window via messages. + +**Parent**: A browser window or frame that creates the window or iframe in which an application runs. (The parent provides a `WindowProxy` object which is used by WCP.) + +**WindowProxy**: An interface defined by the HTML standard that proxies a remote Window object: https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-windowproxy-exotic-object. This is used by WCP. + +**MessagePort**: An interface defined by the HTML Channel Messaging standard that can be used for cross-domain communication between windows. This is used by BCP. + +**Flux Standard Action (FSA)**: An [industry standard](https://github.com/redux-utilities/flux-standard-action) JSON envelope. All WCP and BCP messages are defined as FSAs. + +**SessionStorage**: A JavaScript storage API that persists data for web apps (segregated by domain) within a specific window or tab (SessionStorage is identical to the localStorage API but is tied to a particular window/tab, as defined by the HTML Standard: https://html.spec.whatwg.org/multipage/webstorage.html#dom-sessionstorage.) + +## Establishing Connectivity Using the Web Connection Protocol (WCP) + +The WCP algorithm (coordinated between `getAgent()` and DAs) has four steps. Each step may contain sub-steps. + +1. Connect to DA +2. Validate app identity +3. Persist DesktopAgentDetails to SessionStorage +4. Resolve promise with DesktopAgent interface + +### Connect to DA (Step 1) + +1) Check for a possible navigation or refresh event + + Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDetails` record. If it exists **and the appId matches**, then a navigation or refresh event has occurred and the stored data MUST be used to reestablish a connection to the DA. This ensures that the window maintains a consistent `instanceId` between navigation events within an app. If it doesn't exist, or the appId does not match, then proceed to step (2). + + See [DesktopAgentDetails](./getAgent.d.ts). + + Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `url` exists (immediately opening a hidden iframe). + + If use of persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with "ReestablishConnectionFailed". + +2) Create a race between these four mutually exclusive threads: + + **a) Timeout of 750ms** or the value provided by the `timeout` field + + **b) Preload DAs:** + + If a global (window or globalThis) `fdc3` object exists then return it immediately. If it does not exist then wait for the `fdc3Ready` event. After the `fdc3Ready` event triggers, recheck for the existence of the global `fdc3` object and return it if found. + + **c) Browser-resident DAs:** + + If a `url` exists from step (1) then proceed directly to step (iv), otherwise; + + Locate all "Parents" (`window.opener`, `window.parent` or `window.parent.opener`). If no Parent exists then return null, otherwise simultaneously attempt to contact to each existing Parent with the following steps. + + i) Add a listener (`.addEventListener("message", (event) => {})`) to receive messages from each Parent. + + ii) Send a "Handshake" message to each Parent (set for any origin since parent origin is unknown) with a unique nonce (one time random key). + + iii) Accept the first correct response received (discard all others). Correct responses MUST include the original nonce. Stop the timer when a correct response is received. If the response has transferred a MessagePort (see HTML Channel Messaging API) then proceed to completion of step 2 (below). If the response contains a `url` then proceed to sub-step iv. If neither is provided then reject with "ErrorOnConnect". + + iv) If a `url` has been provided, open a hidden iframe to the URL and listen on the `onload` event. Once the iframe is loaded, repeat steps i through iii substituting the hidden iframe for Parents. + + **Race Resolution** + + If a timeout wins the race then call the failover function (if one was provided to `getAgent()`). + + If the failover function... + + **(I)** ...resolves to a DesktopAgent then return it. + + **(II)** ...resolves to a WindowProxy then race a new timer and return to previous step i. + + **(III)** ...resolves to a URL then race a new timer and return to previous step iv. + + **(IV)** ...resolves to anything else then return null. + + > If the _failover function itself_ results in a timeout then do not rerun the failover function. + + **Completion of Step 2** + + If neither MessagePort nor `DesktopAgent` has been obtained then reject the `getAgent()` promise with "AgentNotFound". + + If a MessagePort was obtained then create a `DesktopAgent` capable of using BCP to communicate with the remote DA (see below). + + Proceed to step 2. + +### Validate App Identity (step 2) + +Apps must identify themselves so that DAs can positively associate them with their corresponding AppD records. Identity is ascertained from either: + +(1) A fully qualified `appId` field (provided to `getAgent()`). + +(2) An `appDUrl` field (provided to `getAgent()`) and then a subsequent http call (or internal lookup) to retrieve that AppD record. + +(3) An `instanceUuid` field provided from SessionStorage (due to a navigation event). + +The DA will validate the app's identity against the app window's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the AppD record referenced by either appId or appDUrl. To ensure the validity of this process, appD records and the application itself SHOULD be served via HTTPS. + +1) If the DA is injected (using the `fdc` global object - Preload DA) then: + + Await the function `fdc3.validateAppIdentity()` if that function exists. `instanceUuid` SHOULD be provided if available from SessionStorage. + + The DA SHOULD validate the app identity against the URL of the window (window.location.href). If successful then it will resolve to an `AppMetaData` object, otherwise it will reject with `IdentityValidationFailed` error. + + After rejection, the DA will not respond to any further FDC3 API calls from the application instance. + + If the `fdc3.validateAppIdentity` function does not exist then simply return the global object as the DesktopAgent. (This should only occur when using legacy Desktop Agents.) + +2) If the DA is browser-resident, then: + + Send a "ValidateAppIdentity" message to the DA via the MessagePort. Include the DesktopAgentDetails record if it exists in SessionStorage. + + The DA will validate the app's identity against the origin of the message. It will return either a "IdentityValidationSucceeded" or "IdentityValidationFailed" message. The `getAgent()` promise should reject on a failure message. + +Success responses (from calls to `validateAppIdentity()` or "ValidateAppIdentityResponse" messages) MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to be persisted in the next step). + +### Persist DesktopAgentDetails to SessionStorage (step 3) + +Once a connection is established, the `DesktopAgentDetails` record that was returned by the DA MUST be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key. + +> Note - SessionStorage is ephemeral, only existing for the duration of the window's life. There is no concern with the key being overwritten or conflicting with other DAs. + +### Resolve promise with DesktopAgent interface (step 4) + +Resolve the `getAgent()` promise with an object containing the `DesktopAgent` from step 1, and `ImplementationMetadata` and `AppMetadata` which were provided by the response from step 2. + +## Communicating using the Browser Communication Protocol (BCP) + +The `DesktopAgent` instantiated by calls to `getAgent()` uses the Browser Communication Protocol (BCP) to interact with the DA that is located in another frame. This protocol is based on exchanges of a standardized set of Flux Standard Actions (FSAs). The protocol is bi-directional. Every FSA that is transmitted results in a corresponding FSA in return, either containing requested data or simply acknowledging receipt. + +BCP uses the HTML Channel Messaging API, communicating via the `MessagePort` object that was established by the Web Connection Protocol (WCP) covered above. + +See [Browser Communication Protocol](./browserCommunicationProtocol.md) + diff --git a/docs/specs/preloadDesktopAgents.md b/docs/specs/preloadDesktopAgents.md new file mode 100644 index 000000000..5d66c5a26 --- /dev/null +++ b/docs/specs/preloadDesktopAgents.md @@ -0,0 +1,41 @@ +# Preload DA Specification + +> Note - The [getAgent() specification](./getAgent.md) relies on Preload DAs behaving as specified in this document. FDC3 apps are now encouraged to use `getAgent()` instead of relying on the existence of the global object. + +## Injecting the global FDC3 object + +A Preload Desktop Agent MUST provide the FDC3 API via a global accessible as `window.fdc3`. Implementors MAY additionally make the API available through modules, imports, or other means. + +The global `window.fdc3` must only be available after the API is ready to use. To enable applications to avoid using the API before it is ready, implementors MUST provide a global `fdc3Ready` event that is fired when the API is ready for use. Implementations should first check for the existence of the FDC3 API and add a listener for this event if it is not found: + +```ts +function fdc3Action() { + // Make some fdc3 API calls here +} + +if (window.fdc3) { + fdc3Action(); +} else { + window.addEventListener('fdc3Ready', fdc3Action); +} +``` + +Since FDC3 is typically available to the whole web application, Desktop Agents are expected to make the [`DesktopAgent`](../api/ref/DesktopAgent) interface available at a global level. + +The global `window.fdc3` SHOULD only be available after the API is ready to use. To prevent the API from being used before it is ready, implementors SHOULD provide an `fdc3Ready` event. + +## validateAppIdentity() + +In order to handle navigation events, Preload DAs SHOULD provide a `validateAppIdentity()` function on the global `fdc3` object (DesktopAgent interface). This function should behave the same as documented in the [Browser Resident DA Authentication Step](./browserResidentDesktopAgents.md#step-2---authentication), allowing the same combinations of appId and appDUrl as are allowed by `getAgent()`. + +Example +```JavaScript +const { instanceUuid, instanceId } = await fdc3.validateAppIdentity({ + appId: "yourApp@yourOrg.com", + instanceUuid: "some generated uuid" +}); +``` + +The function should reject with an error string if the app identity does not match what is expected. + +If a Preload DA does not provide the validateAppIdentity() function this it assumes responsibility for handling navigation and refresh events. diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts new file mode 100644 index 000000000..2ba24c820 --- /dev/null +++ b/src/api/GetAgent.ts @@ -0,0 +1,132 @@ +import { DesktopAgent } from "./DesktopAgent"; +import { ImplementationMetadata } from "./ImplementationMetadata"; +import { AppMetadata } from "./AppMetadata"; + +/** + * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports + * injection or by using the FDC3 Web Connection Protocol (WCP) to establish a connection + * with a browser-resident Desktop Agent. + * + * @param {GetAgentParams} params Required parameters for the function. + * + * @return A promise that resolves to an object which contains a DesktopAgent, or which + * rejects with a string from the AgentError union if a DesktopAgent cannot be established. + * + * @example + * const { desktopAgent: fdc3 } = await getAgent({ + * appId: “myApp@myorg.com/appd”, + * channelSelector: false, + * intentResolver: false + * }); + * + * * @example Using appDUrl + * const { desktopAgent: fdc3 } = await getAgent({ + * appId: “myApp”, + * appDUrl: "myorg.com/appd", + * channelSelector: false, + * intentResolver: false + * }); + */ + +export type GetAgentFunction = ( + params: GetAgentParams, +) => Promise<{ + desktopAgent: DesktopAgent, + implementationMetadata: ImplementationMetadata, + appMetadata: AppMetadata +}>; + +/** + * @typedef {Object} GetAgentParams Type representing parameters passed to the + * getAgent function. + * + * @property {string} appId The fully qualified appId that represents the application. + * (in the form @) + * + * @property {URL} appDUrl A URL that points to an appD record for the application. + * Used as an alternative to providing a fully qualified appId. + * + * @property {number} timeout Number of milliseconds to allow for establishing a + * DesktopAgent. When the timeout expires, the optional provided failover function will be + * run. Default 750. + * + * @property {boolean} channelSelector Flag indicating that the application + * requires getAgent() to create a channel selector UI. Defaults to true. + * + * @property {boolean} intentResolver Flag indicating that the application + * requires getAgent() to create an intent resolver UI. Defaults to true. + * + * @property {function} failover A optional function that can establish connectivity + * to a DesktopAgent if standard mechanisms fail. If a WindowProxy or URL is provided + * then getAgent() will re-run its internal algorithm with those objects (restarting + * the timeout). The function may also simply return a DesktopAgent. + */ + +export type GetAgentParams = { + timeout ?: number, // Defaults to 750 + appId ?: string, + appDUrl ?: string, + channelSelector ?: boolean, // Defaults to true + intentResolver ?: boolean, // Defaults to true + failover ?: (args: GetAgentParams) => Promise +}; + +/** + * Represents the set of errors that may be returned by getAgent() if connectivity + * cannot be established with a DesktopAgent. + * + * "AgentNotFound" - Returned when connectivity to a DA cannot be established. + * + * "InvalidAgent" - Returned when a DA does not conform to the Web Connection Protocol (WCP). + * + * "IdentityValidationFailed" - Returned when the app fails DA identity verification. + * + * "NoAppIdentityProvided" - Returned when the DA refuses a connection from application. + * + * "AccessDenied" - TODO? Returned when the app does not pass one of the required identity parameters indicating an app directory record. + * + * "InvalidFailover" - Returned when the failover function itself, or its resolution is not the right type. + * + * "ReestablishConnectionFailed" - Returned when reestablishment of an instance via persisted StorageSession data fails (e.g. after a page navigation) + * + * "ErrorOnConnect" - Returned when any other error or exception occurs. + */ + +export type AgentError = "AgentNotFound" | "InvalidAgent" | "IdentityValidationFailed" | "NoAppIdentityProvided" | "AccessDenied" | "ErrorOnConnect" | "InvalidFailover" | "ReestablishConnectionFailed"; + +/** + * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. + * getAgent() will use this connection information when it exists to ensure a consistent instanceId. + */ +export type DesktopAgentDetails = { + /** The type of DA. Prevents an inadvertent switch to a different agent.*/ + agentType: WebDesktopAgentType, + + /** May contain the URL that was used to connect to the prior DA. + * This may have been provided by a parent window that has since + * closed. It may therefore be used to open a new window/iframe and restart the DA. */ + url?: string, + + /** The prior appId set by this window. If a appDUrl was previously set then + * this appId will be set to the the representative fully qualified appId. */ + appId: string, + + /** The instanceId that was issued by the DA to this window. */ + instanceId: string, + + /** The instanceUuid that was previously issued by the DA. + * This MUST be passed when connecting to the DA. The DA will use + * this to determine whether the app has previously connected and + * which instance it was. The DA uses this to reissue the same instanceId. + * + * The instanceUuid is secret. It should never be shared with other applications + * and is not available through the FDC3 API. */ + instanceUuid: string +} + +/** Specifies the means by which a connection to the DA is made. + * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. + * "PARENT" - The DA runs in a parent window or iframe. + * "URL" - The DA is loaded as an iframe in the app window. + */ +export type WebDesktopAgentType = "INJECTED" | "PARENT" | "URL"; \ No newline at end of file From fd94343643a339f1fa72e68f2a9b6c79ee3e410f Mon Sep 17 00:00:00 2001 From: Terry Thorsen Date: Thu, 29 Feb 2024 23:35:12 -0500 Subject: [PATCH 002/152] Add BCP definitions --- docs/api/ref/PrivateChannel.md | 2 +- docs/specs/browserCommunicationProtocol.md | 64 ++- src/protocols/bcp.ts | 445 +++++++++++++++++++++ 3 files changed, 509 insertions(+), 2 deletions(-) create mode 100644 src/protocols/bcp.ts diff --git a/docs/api/ref/PrivateChannel.md b/docs/api/ref/PrivateChannel.md index 07168866d..0d1d63200 100644 --- a/docs/api/ref/PrivateChannel.md +++ b/docs/api/ref/PrivateChannel.md @@ -130,7 +130,7 @@ _desktopAgent.AddIntentListener("QuoteStream", async (context, metad ### 'Client-side' example -The 'client' application retrieves a `Channel` by raising an intent with context and awaiting the result. It adds a `ContextListener` so that it can receive messages from it. If a `PrivateChannel` was returned this may in turn trigger a handler added on the 'server-side' with `onAddContextListener()` and start the stream. A listener may also be to clear up if the 'server-side' disconnects from the stream. +The 'client' application retrieves a `Channel` by raising an intent with context and awaiting the result. It adds a `ContextListener` so that it can receive messages from it. If a `PrivateChannel` was returned this may in turn trigger a handler added on the 'server-side' with `onAddContextListener()` and start the stream. The onDisconnect() listener may also be to clean up when the 'server-side' disconnects the stream. Although this interaction occurs entirely in frontend code, we refer to it as the 'client-side' interaction as it requests and receives a stream of responses. diff --git a/docs/specs/browserCommunicationProtocol.md b/docs/specs/browserCommunicationProtocol.md index a5fcb26ff..fc89d40fc 100644 --- a/docs/specs/browserCommunicationProtocol.md +++ b/docs/specs/browserCommunicationProtocol.md @@ -2,4 +2,66 @@ BCP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. -TODO \ No newline at end of file +> Note - We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. + +Type definitions for all BCP messages can be found here: [bcp.ts](TODO). + +## Protocol conventions + +The protocol is divided into groups of messages: + +1) Messages sent from the library to the DA. These typically have a 1:1 correspondence with function calls on `DesktopAgent` and `Channel`, and ancillary functionality such as unsubscribing. + +2) Response messages, sent from the DA to the library. Every message sent from the library to the DA will receive a response. In most cases, the type will simply have "Response" appended. For instance, the response message for `getInfo` is `getInfoResponse`. For all other cases the `BCPAck` message will be the response. Every response's payload will contain an error string if an error occurred, otherwise it will contain the expected data. + +3) Asynchronous "inbound" messages, sent from the DA to the library. These messages are due to actions in other apps, such as an inbound context resulting from another app's broadcast. These messages have the name of the originating message appended with `Inbound`. For example, if another app called `broadcast` then this app would receive a message called `broadcastInbound`. + +Every message has a `meta.messageId`. Initiating messages must set this to be a unique string. Response messages must use the string from their corresponding initiating message. The `messageId` can be used by library and DA to match incoming message responses with their initial requests. + +## Multiplexing + +For any given contextType or intent, the library should only ever send `BCPAddContextListener` or `BCPAddIntentListener` one time. The DA is only responsible for sending any given Context or Intent _once_ to an app. The DA may ignore duplicate listener registrations. + +If the app has registered multiple listeners for these types then it is the responsibility of the _library_ to multiplex the delivered Context, or to choose a specific intent listener. + +When the API calls the unsubscriber for a listener then `BCPRemoveContextListener` or `BCPRemoveIntentListener` should be sent to the DA. + +## Intents + +Refer [Private Channel examples](../api/ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. + +When an app ("client") calls `raiseIntent()` or `raiseIntentByContext()`, the library MUST send the corresponding `BCPRaiseIntent` or `BCPRaiseIntentByContext` message to the DA. + +The DA will resolve the intent and then deliver a `BCPIntentInbound` message to the library in the resolved app ("server"). This message will contain a `responseId` that has been generated by the DA. + +After the message has been sent, the DA will respond back to the "client" app's library with a `BCPRaiseIntentResponse` message containing that `responseId`. + +If the "client" app then calls `getResult()`, then the library will wait until a `BCPIntentResult` message is received with a corresponding `responseId`. It will resolve the `getResult()` call with either a Context or PrivateChannel depending on the contents of the result. + +Meanwhile, if the "server" app's intent handler resolves to a Channel or Context then the library should send a `BCPIntentResult` with the `responseId` that was initially received from `BCPIntentInbound`. + + +## Intent Resolver + +TODO + +## Channels + +Any message related to a channel contains a `channelId` field. It is the responsibility of each party (DA and library) to correlate `channelId` fields with the correct local objects. + +For instance, when the library receives a `BCPBroadcastInbound` message, it should look for the `channelId` field, and only deliver that message to listeners on the corresponding local `Channel` object. + +Likewise, when an app calls `channel.broadcast()` then the library should send the `BCPBroadcast` message with the `channelId` set accordingly. + +## Private Channels + +In general, private channels behave as channels. The DA MUST assign a unique `channelId` in response to `BCPCreatePrivateChannel` messages. `BCPBroadcast` and `BCPAddContextListener` messages can be transmitted with this `channelId`. + +See [Intents](#intents) for the process that is used to established a private channel. + +FDC3's `PrivateChannel` object has some specific functions, each of which has a corresponding BCP message. For instance, `PrivateChannel.onAddContextListener()` can be implemented using the `BCPPrivateChannelOnAddContextListener` message. Each of these types of messages contains a `channelId` which can be used to identify the channel. + +The DA should send `BCPPrivateChannelOnAddContextListener` and `BCPPrivateChannelOnUnsubscribe` messages whenever `BCPAddContextListener` or `BCPRemoveContextListener` is called on a private channel. These will be delivered to the library regardless of whether a client has actually called `onAddContextListener()` and `onUnsubscribe()`. It is the library's responsibility to track these calls and either deliver or discard the messages accordingly. + +Likewise, the DA should send `BCPPrivateChannelOnDisconnect` whenever the `BCPPrivateChannelDisconnect` message is received. It is the library's responsibility to deliver or discard this message. + diff --git a/src/protocols/bcp.ts b/src/protocols/bcp.ts new file mode 100644 index 000000000..d42edc669 --- /dev/null +++ b/src/protocols/bcp.ts @@ -0,0 +1,445 @@ +import { Context } from "../context/ContextTypes"; +import { ContextType } from "../context/ContextType"; +import { AppIdentifier } from "../api/AppIdentifier"; +import { AppMetadata } from "../api/AppMetadata"; +import { AppIntent } from "../api/AppIntent"; +import { ImplementationMetadata } from "../api/ImplementationMetadata"; +import { DisplayMetadata } from "../api/DisplayMetadata"; +import { ContextMetadata } from "../api/ContextMetadata"; + +type BCPMeta = { + messageId: string; +}; + +/** + * Messages initiated by DA (inbound) + */ + +export type BCPBroadcastInbound = { + type: "BroadcastInbound"; + payload: { + context: Context; + channel?: string; + }; + meta: BCPMeta; +}; + +// Sent from library to DA, and then also received by library from DA +export type BCPIntentResult = { + type: "raiseIntentResult"; + payload: { + context?: Context; + channel?: { + channelId: string; + type: "user" | "app" | "private"; + }; + // This will match the responseId from a previous BCPRaiseIntentResponse message + responseId: string; + }; + meta: BCPMeta; +}; + +// Increment subscriber count +export type BCPPrivateChannelOnAddContextListener = { + type: "privateChannelOnAddContextListener"; + payload: { + channelId: string; + contextType?: string; + }; + meta: BCPMeta; +}; + +export type BCPPrivateChannelOnDisconnect = { + type: "privateChannelOnDisconnect"; + payload: { + channelId: string; + }; + meta: BCPMeta; +}; + +// Decrement subscriber count +export type BCPPrivateChannelOnUnsubscribe = { + type: "privateChannelOnUnsubscribe"; + payload: { + channelId: string; + contextType?: string; + }; + meta: BCPMeta; +}; + +export type BCPRaiseIntentInbound = { + type: "raiseIntentInbound"; + payload: { + intent: string; + context: Context; + metadata ?: ContextMetadata; + responseId: string; // generated by DA + }; + meta: BCPMeta; +}; + +/** + * Messages responses from DA + */ + +export type BCPAck = { + type: "ack"; + payload: { + error?: string; + } + meta: BCPMeta; +} + +export type BCPCreatePrivateChannelResponse = { + type: "createPrivateChannelResponse"; + payload: { + error?: string; + channel ?: { + channelId: string; + type: "private"; + displayMetadata ?: DisplayMetadata; + } + }; + meta: BCPMeta; +}; + +export type BCPFindInstancesResponse = { + type: "findInstancesResponse"; + payload: { + error?: string; + instances: Array; + }; + meta: BCPMeta; +}; + +export type BCPFindIntentResponse = { + type: "findIntentResponse"; + payload: { + error?: string; + appIntent?: AppIntent; + }; + meta: BCPMeta; +}; + +export type BCPFindIntentsByContextResponse = { + type: "findIntentsByContextResponse"; + payload: { + error?: string; + appIntents: Array; + }; + meta: BCPMeta; +}; + +export type BCPGetAppMetadataResponse = { + type: "getAppMetadataResponse"; + payload: { + error?: string; + appMetadata?: AppMetadata; + }; + meta: BCPMeta; +}; + +export type BCPGetCurrentChannelResponse = { + type: "getCurrentChannelResponse"; + payload: { + error?: string; + channel?: { + id: string; + displayMetadata ?: DisplayMetadata; + } + }; + meta: BCPMeta; +}; + +export type BCPGetCurrentContextResponse = { + type: "getCurrentContextResponse"; + payload: { + error?: string; + context?: Context; + }; + meta: BCPMeta; +}; + +export type BCPGetInfoResponse = { + type: "getInfoResponse"; + payload: { + error?: string; + implementationMetadata?: ImplementationMetadata; + }; + meta: BCPMeta; +}; + +export type BCPGetOrCreateChannelResponse = { + type: "getOrCreateChannelResponse"; + payload: { + error?: string; + channel ?: { + channelId: string; + type: "user" | "app" | "private"; + displayMetadata ?: DisplayMetadata; + } + }; + meta: BCPMeta; +}; + +export type BCPGetUserChannelsResponse = { + type: "getUserChannelsResponse"; + payload: { + error?: string; + channels: Array<{ + id: string; + displayMetadata ?: DisplayMetadata; + }>; + }; + meta: BCPMeta; +}; + +export type BCPOpenResponse = { + type: "openResponse"; + payload: { + error?: string; + appIdentifier?: AppIdentifier; + }; + meta: BCPMeta; +}; + +export type BCPRaiseIntentResponse = { + type: "raiseIntentResponse"; + payload: { + error?: string; + data?: { + source: AppMetadata; + intent: string; + version?: string; + // Use this to match up future inbound BCPRaiseIntentResult messages + responseId: string; + }; + }; + meta: BCPMeta; +}; + +/** + * Messages initiated by client (outbound) + */ + +export type BCPAddContextListener = { + type: "addContextListener"; + payload: { + contextType: ContextType; + // Set if the listener was added to a specific channel + channel?: string; + }; + meta: BCPMeta; +}; + +export type BCPAddIntentListener = { + type: "addIntentListener"; + payload: { + intent: string; + }; + meta: BCPMeta; +}; + +export type BCPBroadcast = { + type: "broadcast"; + payload: { + context: Context; + channelId?: string; + }; + meta: BCPMeta; +}; + +export type BCPCreatePrivateClient = { + type: "createPrivateClient"; + meta: BCPMeta; +}; + +export type BCPFindInstances = { + type: "findInstances"; + app: AppIdentifier; + meta: BCPMeta; +}; + +export type BCPFindIntent = { + type: "findIntent"; + payload: { + intent: string; + context?: Context; + resultType?: string; + }; + meta: BCPMeta; +}; + +export type BCPFindIntentsByContext = { + type: "findIntentsByContext"; + payload: { + context?: Context; + resultType?: string; + }; + meta: BCPMeta; +}; + +export type BCPGetAppMetadata = { + type: "getAppMetadata"; + payload: { + app: AppIdentifier; + } + meta: BCPMeta; +}; + +export type BCPGetCurrentChannel = { + type: "getCurrentChannel"; + meta: BCPMeta; +}; + +export type BCPGetCurrentContext = { + type: "getCurrentContext"; + payload: { + contextType?: string; + channelId?: string; + }; + meta: BCPMeta; +}; + +export type BCPGetInfo = { + type: "getInfo"; + meta: BCPMeta; +}; + +export type BCPGetOrCreateChannel = { + type: "getOrCreateChannel"; + payload: { + channelId: string + } + meta: BCPMeta; +}; + +export type BCPGetUserChannels = { + type: "getUserChannels"; + meta: BCPMeta; +}; + +export type BCPJoinUserChannel = { + type: "joinUserChannel"; + payload: { + channelId: string; + }; + meta: BCPMeta; +}; + +export type BCPLeaveCurrentChannel = { + type: "leaveCurrentChannel"; + meta: BCPMeta; +}; + +export type BCPOpen = { + type: "open"; + payload: { + app: AppIdentifier | string; + context?: Context; + }; + meta: BCPMeta; +}; + +export type BCPPrivateChannelDisconnect = { + type: "privateChannelDisconnect"; + payload: { + channelId: string; + }; + meta: BCPMeta; +}; + +export type BCPRaiseIntent = { + type: "raiseIntent"; + payload: { + intent: string | null; + context: Context; + app ?: AppIdentifier; + }; + meta: BCPMeta; +}; + +export type BCPRaiseIntentForContext = { + type: "raiseIntentForContext"; + payload: { + context?: Context; + app?: AppIdentifier + }; + meta: BCPMeta; +}; + +export type BCPRemoveContextListener = { + type: "removeContextListener"; + payload: { + contextType?: ContextType; + channel?: string; + }; + meta: BCPMeta; +}; + +export type BCPRemoveIntentListener = { + type: "removeIntentListener"; + payload: { + intent: string; + }; + meta: BCPMeta; +}; + +/** + * Intent resolver protocol + */ + +TODO define these and add them to the corresponding sections + +export type BCPMessageInbound = + BCPBroadcastInbound | + BCPIntentResult | + BCPPrivateChannelOnAddContextListener | + BCPPrivateChannelOnDisconnect | + BCPPrivateChannelOnUnsubscribe | + BCPRaiseIntentInbound; + +export type BCPMessageResponse = + BCPAck | + BCPCreatePrivateChannelResponse | + BCPFindInstancesResponse | + BCPFindIntentResponse | + BCPFindIntentsByContextResponse | + BCPGetAppMetadataResponse | + BCPGetCurrentChannelResponse | + BCPGetCurrentContextResponse | + BCPGetInfoResponse | + BCPGetOrCreateChannelResponse | + BCPGetUserChannelsResponse | + BCPOpenResponse | + BCPRaiseIntentResponse; + +export type BCPMessageOutbound = + BCPAddContextListener | + BCPAddIntentListener | + BCPBroadcast | + BCPCreatePrivateClient | + BCPFindInstances | + BCPFindIntent | + BCPFindIntentsByContext | + BCPGetAppMetadata | + BCPGetCurrentChannel | + BCPGetCurrentContext | + BCPGetInfo | + BCPGetOrCreateChannel | + BCPGetUserChannels | + BCPJoinUserChannel | + BCPLeaveCurrentChannel | + BCPOpen | + BCPPrivateChannelDisconnect | + BCPRaiseIntent | + BCPRaiseIntentForContext | + BCPRemoveContextListener | + BCPRemoveIntentListener; + +export type BCPMessage = BCPMessageInbound | BCPMessageOutbound | BCPMessageResponse; + +// Returns a new type that equals the member of an original type +export type Member = T[K]; + +export type BCPType = Member; \ No newline at end of file From 253dc93c1a43b3710b30a9a96ba18be8def9dbaa Mon Sep 17 00:00:00 2001 From: Terry Thorsen Date: Mon, 4 Mar 2024 15:04:47 -0500 Subject: [PATCH 003/152] Finish protocols --- CHANGELOG.md | 4 +- docs/api/ref/GetAgent.md | 125 +++++++++++++++++++++ docs/specs/browserCommunicationProtocol.md | 6 +- docs/specs/browserResidentDesktopAgents.md | 29 +++-- docs/specs/getAgent.md | 9 +- src/api/DesktopAgent.ts | 14 ++- src/api/GetAgent.ts | 14 +-- src/protocols/bcp.ts | 110 ++++++++++++++---- src/protocols/wcp.ts | 42 +++++++ 9 files changed, 312 insertions(+), 41 deletions(-) create mode 100644 docs/api/ref/GetAgent.md create mode 100644 src/protocols/wcp.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d7a65362..32b1f85ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added * Specifications for getAgent() and Browser-Resident Desktop Agents. -* Specification for Preload Desktop Agents. This content was previously in the supported platforms section. It had been revised and amended to include recommended behavior related to the new ValidateAppIdentity() function. +* Specification for Preload Desktop Agents. This content was previously in the supported platforms section. It had been revised and amended to include recommended behavior related to the new validateAppIdentity() function. +* Added optional validateAppIdentity() function to DesktopAgent interface. * Typescript definitions for getAgent() and related types * Typescript definitions for Browser Communication Protocol (BCP). These constitute the internal "wire protocol" that the "@finos/fdc3" library uses to communicate with Browser-Resident DAs. +* Typescript definitions for Web Connection Protocol (WCP). These constitute the messages used to establish connectivity between "@finos/fdc3" and a Browser-Resident DA. ### Changed diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md new file mode 100644 index 000000000..1d7191a0a --- /dev/null +++ b/docs/api/ref/GetAgent.md @@ -0,0 +1,125 @@ + +```Typescript +/** + * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports + * injection or by using the FDC3 Web Connection Protocol (WCP) to establish a connection + * with a browser-resident Desktop Agent. + * + * @param {GetAgentParams} params Required parameters for the function. + * + * @return A promise that resolves to an object which contains a DesktopAgent, or which + * rejects with a string from the AgentError union if a DesktopAgent cannot be established. + * + * @example + * const { desktopAgent: fdc3 } = await getAgent({ + * appId: “myApp@myorg.com/appd” + * }); + * + * * @example Using appDUrl + * const { desktopAgent: fdc3 } = await getAgent({ + * appId: “myApp”, + * appDUrl: "myorg.com/appd" + * }); + */ + +export type GetAgentFunction = ( + params: GetAgentParams, +) => Promise<{ + desktopAgent: DesktopAgent, + implementationMetadata: ImplementationMetadata, + appMetadata: AppMetadata +}>; + +/** + * @typedef {Object} GetAgentParams Type representing parameters passed to the + * getAgent function. + * + * @property {string} appId The fully qualified appId that represents the application. + * (in the form @) + * + * @property {URL} appDUrl A URL that points to an appD record for the application. + * Used as an alternative to providing a fully qualified appId. + * + * @property {number} timeout Number of milliseconds to allow for establishing a + * DesktopAgent. When the timeout expires, the optional provided failover function will be + * run. Default 750. + * + * @property {boolean} channelSelector Flag indicating that the application + * requires getAgent() to create a channel selector UI. Defaults to true. + * + * @property {boolean} intentResolver Flag indicating that the application + * requires getAgent() to create an intent resolver UI. Defaults to true. + * + * @property {function} failover A optional function that can establish connectivity + * to a DesktopAgent if standard mechanisms fail. If a WindowProxy or URL is provided + * then getAgent() will re-run its internal algorithm with those objects (restarting + * the timeout). The function may also simply return a DesktopAgent. + */ + +export type GetAgentParams = { + timeout ?: number, // Defaults to 750 + appId ?: string, + appDUrl ?: string, + failover ?: (args: GetAgentParams) => Promise +}; + +/** + * Represents the set of errors that may be returned by getAgent() if connectivity + * cannot be established with a DesktopAgent. + * + * "AgentNotFound" - Returned when connectivity to a DA cannot be established. + * + * "InvalidAgent" - Returned when a DA does not conform to the Web Connection Protocol (WCP). + * + * "IdentityValidationFailed" - Returned when the app fails DA identity verification. + * + * "NoAppIdentityProvided" - Returned when the DA refuses a connection from application. + * + * "AccessDenied" - TODO? Returned when the app does not pass one of the required identity parameters indicating an app directory record. + * + * "InvalidFailover" - Returned when the failover function itself, or its resolution is not the right type. + * + * "ReestablishConnectionFailed" - Returned when reestablishment of an instance via persisted StorageSession data fails (e.g. after a page navigation) + * + * "ErrorOnConnect" - Returned when any other error or exception occurs. + */ + +export type AgentError = "AgentNotFound" | "InvalidAgent" | "IdentityValidationFailed" | "NoAppIdentityProvided" | "AccessDenied" | "ErrorOnConnect" | "InvalidFailover" | "ReestablishConnectionFailed"; + +/** + * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. + * getAgent() will use this connection information when it exists to ensure a consistent instanceId. + */ +export type DesktopAgentDetails = { + /** The type of DA. Prevents an inadvertent switch to a different agent.*/ + agentType: WebDesktopAgentType, + + /** May contain the URL that was used to connect to the prior DA. + * This may have been provided by a parent window that has since + * closed. It may therefore be used to open a new window/iframe and restart the DA. */ + url?: string, + + /** The prior appId set by this window. If a appDUrl was previously set then + * this appId will be set to the the representative fully qualified appId. */ + appId: string, + + /** The instanceId that was issued by the DA to this window. */ + instanceId: string, + + /** The instanceUuid that was previously issued by the DA. + * This MUST be passed when connecting to the DA. The DA will use + * this to determine whether the app has previously connected and + * which instance it was. The DA uses this to reissue the same instanceId. + * + * The instanceUuid is secret. It should never be shared with other applications + * and is not available through the FDC3 API. */ + instanceUuid: string +} + +/** Specifies the means by which a connection to the DA is made. + * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. + * "PARENT" - The DA runs in a parent window or iframe. + * "URL" - The DA is loaded as an iframe in the app window. + */ +export type WebDesktopAgentType = "INJECTED" | "PARENT" | "URL"; +``` \ No newline at end of file diff --git a/docs/specs/browserCommunicationProtocol.md b/docs/specs/browserCommunicationProtocol.md index fc89d40fc..09baa1cc2 100644 --- a/docs/specs/browserCommunicationProtocol.md +++ b/docs/specs/browserCommunicationProtocol.md @@ -43,10 +43,14 @@ Meanwhile, if the "server" app's intent handler resolves to a Channel or Context ## Intent Resolver -TODO +The DA should send `BCPResolveIntent` if it requires an external UI for intent resolution. This MUST include the list of available apps which are capable of being launched to handle the intent, and it MUST include the list of open apps which are capable of handling the intent. The "@finos/fdc3" library will present UI to the end user, and then will respond with a `BCPResolveIntentResponse` containing the user's choice. + +DAs are free to provide their own intent resolution UIs if they have this capability. ## Channels +The DA should send `BCPInitializeChannelSelector` if it requires the app to provide UI for channel selection. The "@finos/fdc3" library will provide the UI when this message is received. + Any message related to a channel contains a `channelId` field. It is the responsibility of each party (DA and library) to correlate `channelId` fields with the correct local objects. For instance, when the library receives a `BCPBroadcastInbound` message, it should look for the `channelId` field, and only deliver that message to listeners on the corresponding local `Channel` object. diff --git a/docs/specs/browserResidentDesktopAgents.md b/docs/specs/browserResidentDesktopAgents.md index 2597c6ecb..10c424c89 100644 --- a/docs/specs/browserResidentDesktopAgents.md +++ b/docs/specs/browserResidentDesktopAgents.md @@ -60,7 +60,7 @@ window.addEventListener("message", (event) => { ## Responding to app communications with Browser Communication Protocol (BCP) -BCP processing should begin by setting up an inactive connection instance. This instance will become active after the first BCP "ValidateAppIdentity" message is received and processed (or deleted if it fails). It is important to remember the WindowProxy (event.source). +BCP processing should begin by setting up an inactive connection instance. This instance will become active after the first BCP "WCPValidateAppIdentity" message is received and processed (or deleted if it fails). It is important to remember the WindowProxy (event.source). An `instanceUuid` should be established at this point. This will be used in following steps. @@ -83,9 +83,9 @@ const startBCPProcessing(event, channel) => { ### Step 1 - Validating a connection -The first BCP message received should be a "ValidateAppIdentity" message. With this message, the DA establishes or denies the connection. +The first BCP message received should be a "WCPValidateAppIdentity" message. With this message, the DA establishes or denies the connection. -1) If the message contains an `instanceUuid` field then the app window might have navigated or refreshed and simply require reconnecting on a previously established instance. The DA SHOULD reestablish the connection if the `instanceUuid`, `appId`, WindowProxy and origin match what is already on record. When a connection is reestablished the DA MUST respond with a valid "IdentityValidationSucceeded" message. +1) If the message contains an `instanceUuid` field then the app window might have navigated or refreshed and simply require reconnecting on a previously established instance. The DA SHOULD reestablish the connection if the `instanceUuid`, `appId`, WindowProxy and origin match what is already on record. When a connection is reestablished the DA MUST respond with a valid "WCPValidateAppIdentityResponse" message. 2) If the message does not contain an `instanceUuid`, or the record does not match, then proceed to authenticate the application. @@ -114,7 +114,7 @@ const processValidateAppIdentityBCP = (connection, e) => { connection.appId = maybeExistingConnection.appId; connection.appMetadata = maybeExistingConnection.appMetadata; connection.channel.port1.postMessage({ - type: "IdentityValidationSucceeded", + type: "validateAppIdentityResponse", payload: { desktopAgentDetails: { agentType: "PARENT", @@ -144,11 +144,11 @@ Each of these should result in a request to "https://myapp.com/appd/v2/apps/myAp `appId="myApp", appDUrl="https://muyapp.com/appd"` `appDUrl="https://myapp.com/appd/v2/apps/myApp"` -The DA MUST validate the app's identity against the application's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the _AppD record constructed from the ValidateAppIdentity message_. +The DA MUST validate the app's identity against the application's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the _AppD record constructed from the WCPValidateAppIdentity message_. > Note - The AppD record provided by the app may be different from the one that the DA used to launch the app. This is because a DA may launch apps from its own app directory or even without a directory. However, the responsibility to provide application identity _rests with its publisher_ (the entity that is _hosting_ the app) and hence DA's MUST reference the AppD record that is provided by the application itself for verification. DAs MAY choose to implement _additional_ identity verification procedures (such as comparison with their own configured app directories) and MAY choose to deny access to an application for any reason. -If authentication succeeds then the DA MUST respond with a "IdentityValidationSucceeded" message which MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to support reconnecting after window navigation). +If authentication succeeds then the DA MUST respond with a "WCPValidateAppIdentityResponse" message which MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to support reconnecting after window navigation). ```JavaScript const authenticateApp = (connection, e) => { @@ -167,7 +167,7 @@ const authenticateApp = (connection, e) => { connection.instanceId = uuid(); connection.appMetadata = new AppMetadata(); // To be set by the DA connection.channel.port1.postMessage({ - type: "IdentityValidationSucceeded", + type: "WCPValidateAppIdentityResponse", payload: { desktopAgentDetails: { agentType: "PARENT", @@ -181,7 +181,7 @@ const authenticateApp = (connection, e) => { }, connection.origin) } else { connection.port.postMessage({ - type: "IdentityValidationFailed", + type: "WCPValidateAppIdentityResponse", payload: { error: `Origin "${origin}" for "${appId}" didn't match AppD record` } @@ -192,10 +192,12 @@ const authenticateApp = (connection, e) => { ### Step 3 - DesktopAgent Operations (BCP) -TODO +Each message should be responded to with its corresponding response when a response should contain data, or with `BCPAck` if only an acknowledgement is required. See [Browser Communication Protocol](./browserCommunicationProtocol.md) +See bcp.ts for a full list of BCP messages. + ## Implementing DAs in hidden iframes DA providers can leverage hidden iframes to establish a communication mechanism that is independent of Parent windows. This approach allows apps to connect to a DA even when they were not opened by that DA. @@ -208,6 +210,15 @@ The hidden iframe url can be provided in two ways: The same WCP mechanism is used to connect to DAs located in hidden iframes. Likewise, the same BCP messages are used for communications. +## UI Channel Selector and Intent Resolver + +A DA may implement its own Channel Selector and Intent Resolver or may utilize the one provided by "@finos/fdc3" via `getAgent()` ("built-in UI"). For instance, a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()`. The built-in UI can be leveraged in this circumstance. Send the `BCPResolveIntent` and `BCPInitializeChannelSelector` messages to invoke the built-in UI. + +## Disconnects + +DAs are responsible for tracking when app windows close by checking `win.closed` in a loop. + +https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128 diff --git a/docs/specs/getAgent.md b/docs/specs/getAgent.md index aeaaf2dcf..35d50bf2b 100644 --- a/docs/specs/getAgent.md +++ b/docs/specs/getAgent.md @@ -115,11 +115,11 @@ The DA will validate the app's identity against the app window's origin (protoco 2) If the DA is browser-resident, then: - Send a "ValidateAppIdentity" message to the DA via the MessagePort. Include the DesktopAgentDetails record if it exists in SessionStorage. + Send a "WCPValidateAppIdentity" message to the DA via the MessagePort. Include the DesktopAgentDetails record if it exists in SessionStorage. The DA will validate the app's identity against the origin of the message. It will return either a "IdentityValidationSucceeded" or "IdentityValidationFailed" message. The `getAgent()` promise should reject on a failure message. -Success responses (from calls to `validateAppIdentity()` or "ValidateAppIdentityResponse" messages) MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to be persisted in the next step). +Success responses (from calls to `validateAppIdentity()` or "WCPValidateAppIdentityResponse" messages) MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to be persisted in the next step). ### Persist DesktopAgentDetails to SessionStorage (step 3) @@ -139,3 +139,8 @@ BCP uses the HTML Channel Messaging API, communicating via the `MessagePort` obj See [Browser Communication Protocol](./browserCommunicationProtocol.md) +## Built in UI (Channel Selector and Intent Resolver) + +`getAgent()` MUST provide UI for channel selector and intent resolution. These can be OPTIONALLY utilized by the DA. The DA will send the `BCPInitializeChannelSelector` message if it does not have its own way to manage channel selection. It will send `BCPResolveIntent` if it does not have a way to present an intent resolver UI. + +`getAgent()` SHOULD implement UI within the app's DOM, for instance with an overlay for channel selector and a modal dialog for intent resolution. diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts index f9b989c2b..11b861501 100644 --- a/src/api/DesktopAgent.ts +++ b/src/api/DesktopAgent.ts @@ -13,6 +13,7 @@ import { ImplementationMetadata } from './ImplementationMetadata'; import { PrivateChannel } from './PrivateChannel'; import { AppIdentifier } from './AppIdentifier'; import { AppMetadata } from './AppMetadata'; +import { DesktopAgentDetails } from './GetAgent'; /** * A Desktop Agent is a desktop component (or aggregate of components) that serves as a @@ -523,8 +524,19 @@ export interface DesktopAgent { */ getAppMetadata(app: AppIdentifier): Promise; + /** + * Use by getAgent() to validate app identity. Apps should not call this function directly. + * + * See getAgent.md for instructions. + * + * Either appId, appDUrl, or both may be provided. This logic is covered int supported_platforms.md. + * + * This function is optional but recommended. + */ + validateAppIdentity?({appId, appDUrl, instanceUuid} : { appId?: string, appDUrl?: string, instanceUuid?:string}) : Promise + //--------------------------------------------------------------------------------------------- - //Deprecated function signatures + // Deprecated function signatures //--------------------------------------------------------------------------------------------- /** diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 2ba24c820..cf58562bc 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -1,3 +1,7 @@ +/** + * Typescript related to the getAgent() function. Note that GetAgent.md is a copy of this file which has been modified + * for display in markdown format. + */ import { DesktopAgent } from "./DesktopAgent"; import { ImplementationMetadata } from "./ImplementationMetadata"; import { AppMetadata } from "./AppMetadata"; @@ -14,17 +18,13 @@ import { AppMetadata } from "./AppMetadata"; * * @example * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp@myorg.com/appd”, - * channelSelector: false, - * intentResolver: false + * appId: “myApp@myorg.com/appd” * }); * * * @example Using appDUrl * const { desktopAgent: fdc3 } = await getAgent({ * appId: “myApp”, - * appDUrl: "myorg.com/appd", - * channelSelector: false, - * intentResolver: false + * appDUrl: "myorg.com/appd" * }); */ @@ -66,8 +66,6 @@ export type GetAgentParams = { timeout ?: number, // Defaults to 750 appId ?: string, appDUrl ?: string, - channelSelector ?: boolean, // Defaults to true - intentResolver ?: boolean, // Defaults to true failover ?: (args: GetAgentParams) => Promise }; diff --git a/src/protocols/bcp.ts b/src/protocols/bcp.ts index d42edc669..ed1104073 100644 --- a/src/protocols/bcp.ts +++ b/src/protocols/bcp.ts @@ -1,3 +1,6 @@ +/** + * Browser Communication Protocol (BCP) is used for communication between the "@finos/fdc3" library and Browser-Resident DAs. + */ import { Context } from "../context/ContextTypes"; import { ContextType } from "../context/ContextType"; import { AppIdentifier } from "../api/AppIdentifier"; @@ -93,12 +96,13 @@ export type BCPAck = { export type BCPCreatePrivateChannelResponse = { type: "createPrivateChannelResponse"; payload: { - error?: string; - channel ?: { + channel : { channelId: string; type: "private"; displayMetadata ?: DisplayMetadata; } + } | { + error: string; }; meta: BCPMeta; }; @@ -106,8 +110,9 @@ export type BCPCreatePrivateChannelResponse = { export type BCPFindInstancesResponse = { type: "findInstancesResponse"; payload: { - error?: string; instances: Array; + } | { + error: string; }; meta: BCPMeta; }; @@ -115,8 +120,9 @@ export type BCPFindInstancesResponse = { export type BCPFindIntentResponse = { type: "findIntentResponse"; payload: { - error?: string; appIntent?: AppIntent; + } | { + error: string; }; meta: BCPMeta; }; @@ -124,8 +130,9 @@ export type BCPFindIntentResponse = { export type BCPFindIntentsByContextResponse = { type: "findIntentsByContextResponse"; payload: { - error?: string; appIntents: Array; + } | { + error: string; }; meta: BCPMeta; }; @@ -133,8 +140,9 @@ export type BCPFindIntentsByContextResponse = { export type BCPGetAppMetadataResponse = { type: "getAppMetadataResponse"; payload: { - error?: string; appMetadata?: AppMetadata; + } | { + error: string; }; meta: BCPMeta; }; @@ -142,11 +150,13 @@ export type BCPGetAppMetadataResponse = { export type BCPGetCurrentChannelResponse = { type: "getCurrentChannelResponse"; payload: { - error?: string; + // Channel will be undefined if not on a current channel channel?: { id: string; displayMetadata ?: DisplayMetadata; } + } | { + error: string; }; meta: BCPMeta; }; @@ -154,8 +164,10 @@ export type BCPGetCurrentChannelResponse = { export type BCPGetCurrentContextResponse = { type: "getCurrentContextResponse"; payload: { - error?: string; + // context will be undefined if no current context context?: Context; + } | { + error: string; }; meta: BCPMeta; }; @@ -163,8 +175,9 @@ export type BCPGetCurrentContextResponse = { export type BCPGetInfoResponse = { type: "getInfoResponse"; payload: { - error?: string; - implementationMetadata?: ImplementationMetadata; + implementationMetadata: ImplementationMetadata; + } | { + error: string; }; meta: BCPMeta; }; @@ -172,12 +185,13 @@ export type BCPGetInfoResponse = { export type BCPGetOrCreateChannelResponse = { type: "getOrCreateChannelResponse"; payload: { - error?: string; - channel ?: { + channel : { channelId: string; type: "user" | "app" | "private"; displayMetadata ?: DisplayMetadata; } + } | { + error: string; }; meta: BCPMeta; }; @@ -185,11 +199,12 @@ export type BCPGetOrCreateChannelResponse = { export type BCPGetUserChannelsResponse = { type: "getUserChannelsResponse"; payload: { - error?: string; channels: Array<{ id: string; displayMetadata ?: DisplayMetadata; }>; + } | { + error: string; }; meta: BCPMeta; }; @@ -197,8 +212,9 @@ export type BCPGetUserChannelsResponse = { export type BCPOpenResponse = { type: "openResponse"; payload: { - error?: string; - appIdentifier?: AppIdentifier; + appIdentifier: AppIdentifier; + } | { + error: string; }; meta: BCPMeta; }; @@ -206,14 +222,15 @@ export type BCPOpenResponse = { export type BCPRaiseIntentResponse = { type: "raiseIntentResponse"; payload: { - error?: string; - data?: { + data: { source: AppMetadata; intent: string; version?: string; // Use this to match up future inbound BCPRaiseIntentResult messages responseId: string; }; + } | { + error: string; }; meta: BCPMeta; }; @@ -386,10 +403,65 @@ export type BCPRemoveIntentListener = { }; /** - * Intent resolver protocol + * Library provided UI messages */ -TODO define these and add them to the corresponding sections +// Sent from DA to App when an intent needs resolution and it does +// not have the ability to provide a graphical intent resolver +export type BCPResolveIntent = { + type: "resolveIntent", + payload: { + // The intent to resolve + intent: { + name: string; + displayName: string; + }; + + // If raiseIntentForContext + context?: Context; + + // The DA provided list of launchable apps which may resolve this intent type + launchableApps: { + meta: AppMetadata; + appId: string; + }[]; + + // The DA provided lists of open apps which are registered to receive this intent type + openApps: { + meta: AppMetadata; + instanceId ?: string; + }[]; + + // the originatingAppName may be displayed by the UI Resolver to indicate the source of the request. + originatingAppName?: string; + }; + meta: BCPMeta; +} + +// Response from App to DA with intent resolution +export type BCPResolveIntentResponse = { + type: "resolveIntentResponse", + payload: { + error?: string; + + // appId is set if intent should be delivered to a launchable app + appId?: string; + + // instanceId is set if intent should be delivered to open app + instanceId?: string; + }; + meta: BCPMeta; +} + +// Sent by the DA to the App to enable the built-in channel selector. `id` can optionally be set in order +// to initialize a specific channel (for instance when rehydrating an app from previous persistence) +export type BCPinitializeChannelSelector = { + type: "initializeChannelSelector", + payload: { + // The id of the channel which should be enabled, if one should be initially set + id?: string; + } +} export type BCPMessageInbound = BCPBroadcastInbound | diff --git a/src/protocols/wcp.ts b/src/protocols/wcp.ts new file mode 100644 index 000000000..f3f0e8d07 --- /dev/null +++ b/src/protocols/wcp.ts @@ -0,0 +1,42 @@ +/** + * Web Connection Protocol (WCP) is used to establish connectivity between "@finos/fdc3" and Browser-Resident DAs. + */ + +import { AppMetadata } from "../api/AppMetadata"; +import { DesktopAgentDetails } from "../api/GetAgent"; +import { ImplementationMetadata } from "../api/ImplementationMetadata"; + +export type WCPHandshake = { + type: "handshake", + payload: { + nonce: string; + } +} + +// Note, the MessagePort is _transferred_ using postMessage. It is not included in the response packet. +export type WCPHandshakeResponse = { + type: "handshakeResponse", + payload: { + nonce: string; + // If set, then getAgent() will open a hidden iframe and restart negotiation with that frame + url?: string; + } +} + +export type WCPValidateAppIdentity = { + type: "validateAppIdentity", + payload: { + desktopAgentDetails?: DesktopAgentDetails; + } +} + +export type WCPValidateAppIdentityResponse = { + type: "validateAppIdentityResponse", + payload: { + desktopAgentDetails: DesktopAgentDetails; + appMetaData: AppMetadata; + implementationMetadata: ImplementationMetadata; + } | { + error: string; + } +} \ No newline at end of file From 28c7a9589a91e7ed5c6ff5211adc88136ec68984 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 15 Apr 2024 12:20:09 +0100 Subject: [PATCH 004/152] Update docs/specs/getAgent.md --- docs/specs/getAgent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specs/getAgent.md b/docs/specs/getAgent.md index 35d50bf2b..946d21e53 100644 --- a/docs/specs/getAgent.md +++ b/docs/specs/getAgent.md @@ -10,7 +10,7 @@ **getAgent()**: The library function provided by `@finos/fdc3` that discovers and establishes communication to DAs. It may (1) return a reference to an injected `DesktopAgent` instance, (2) use the FDC3 Web Connection Protocol (WCP) to discover a DA (e.g. in a "parent" window or frame) and return a `DesktopAgent` instance that communicates with the DA using the FDC3 Browser Communication Protocol (BCP), or (3) run an application provided failover function that provides direct or indirect access to a `DesktopAgent`. -**Web Connection Protocol (WCP)**: A protocol for discovering and establishing communications with a DA. Ths includes a proscribed algorithm as well as some standard messages that are transmitted using `window.postMessage`. +**Web Connection Protocol (WCP)**: A protocol for discovering and establishing communications with a DA. Ths includes a prescribed algorithm as well as some standard messages that are transmitted using `window.postMessage`. **Browser Communication Protocol (BCP)**: A protocol that uses the standard HTML Channel Messaging API (MessagePort) to communicate with a DA in a remote iframe or window via messages. From ac7723f452e052da9b44d9876486fb324411e5f8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 16 Apr 2024 18:37:52 +0100 Subject: [PATCH 005/152] 50% complete json schemas for api wire protocol messages --- .../PrivateChannel.broadcastEvent.schema.json | 0 ...ivateChannel.disconnectRequest.schema.json | 0 ...vateChannel.disconnectResponse.schema.json | 0 ...hannel.eventListenerAddedEvent.schema.json | 0 ...nnel.eventListenerRemovedEvent.schema.json | 0 ...nnel.onAddContextListenerEvent.schema.json | 0 ...ivateChannel.onDisconnectEvent.schema.json | 0 ...vateChannel.onUnsubscribeEvent.schema.json | 0 schemas/api/README.md | 19 +++-- .../api/addContextListenerRequest.schema.json | 0 .../addContextListenerResponse.schema.json | 0 .../api/addIntentListenerRequest.scheam.json | 0 .../api/addIntentListenerResponse.schema.json | 0 schemas/api/agentEventMessage.schema.json | 46 +++++++++++ schemas/api/agentResponseMessage.schema.json | 81 +++++++++++++++++++ schemas/api/appRequest.schema.json | 63 +++++++++++++++ schemas/api/broadcastRequest.schema.json | 52 ++++++++++++ schemas/api/broadcastResponse.schema.json | 29 +++++++ schemas/api/channelChangedEvent..schema.json | 0 schemas/api/common.schema.json | 59 ++++++++++++++ ...textListenerUnsubscribeRequest.schema.json | 0 ...extListenerUnsubscribeResponse.schema.json | 0 .../createPrivateChannelRequest.schema.json | 37 +++++++++ .../createPrivateChannelResponse.schema.json | 64 +++++++++++++++ schemas/api/findInstancesRequest.schema.json | 42 ++++++++++ schemas/api/findInstancesResponse.schema.json | 77 ++++++++++++++++++ schemas/api/findIntentRequest.schema.json | 53 ++++++++++++ schemas/api/findIntentResponse.schema.json | 72 +++++++++++++++++ .../findIntentsByContextRequest.schema.json | 44 ++++++++++ .../findIntentsByContextResponse.schema.json | 76 +++++++++++++++++ schemas/api/getAppMetadataRequest.schema.json | 44 ++++++++++ .../api/getAppMetadataResponse.schema.json | 73 +++++++++++++++++ .../api/getCurrentChannelRequest.schema.json | 37 +++++++++ .../api/getCurrentChannelResponse.schema.json | 67 +++++++++++++++ schemas/api/getInfoRequest.schema.json | 37 +++++++++ schemas/api/getInfoResponse.schema.json | 64 +++++++++++++++ .../api/getOrCreateChannelRequest.schema.json | 44 ++++++++++ .../getOrCreateChannelResponse.schema.json | 64 +++++++++++++++ .../api/getUserChannelsRequest.schema.json | 37 +++++++++ .../api/getUserChannelsResponse.schema.json | 67 +++++++++++++++ ...tentListenerUnsubscribeRequest.schema.json | 0 ...entListenerUnsubscribeResponse.schema.json | 0 .../api/joinUserChannelRequest.schema.json | 0 .../api/joinUserChannelsResponse.schema.json | 0 .../leaveCurrentChannelRequest.schema.json | 0 .../leaveCurrentChannelResponse.schema.json | 0 schemas/api/openRequest.schema.json | 0 schemas/api/openResponse.schema.json | 0 .../raiseIntentForContextRequest.schema.json | 0 .../raiseIntentForContextResponse.schema.json | 0 schemas/api/raiseIntentRequest.schema.json | 0 schemas/api/raiseIntentResponse.schema.json | 0 .../api/raiseIntentResultResponse.schema.json | 0 .../bridging/agentErrorResponse.schema.json | 2 +- schemas/bridging/agentRequest.schema.json | 4 +- schemas/bridging/agentResponse.schema.json | 6 +- .../bridging/bridgeErrorResponse.schema.json | 8 +- schemas/bridging/bridgeRequest.schema.json | 4 +- schemas/bridging/bridgeResponse.schema.json | 6 +- .../broadcastAgentRequest.schema.json | 20 +---- schemas/bridging/common.schema.json | 51 +----------- schemas/bridging/connectionStep.schema.json | 6 +- .../bridging/connectionStep2Hello.schema.json | 2 +- .../connectionStep3Handshake.schema.json | 4 +- ...ctionStep4AuthenticationFailed.schema.json | 6 +- ...tionStep6ConnectedAgentsUpdate.schema.json | 6 +- ...indInstancesAgentErrorResponse.schema.json | 17 +--- .../findInstancesAgentRequest.schema.json | 13 +-- .../findInstancesAgentResponse.schema.json | 23 +----- .../findIntentAgentErrorResponse.schema.json | 17 +--- .../findIntentAgentRequest.schema.json | 22 +---- .../findIntentAgentResponse.schema.json | 13 +-- ...ntsByContextAgentErrorResponse.schema.json | 17 +--- ...ndIntentsByContextAgentRequest.schema.json | 13 +-- ...dIntentsByContextAgentResponse.schema.json | 17 +--- ...tAppMetadataAgentErrorResponse.schema.json | 17 +--- .../getAppMetadataAgentRequest.schema.json | 23 +++--- .../getAppMetadataAgentResponse.schema.json | 13 +-- ...EventListenerAddedAgentRequest.schema.json | 2 +- ...entListenerRemovedAgentRequest.schema.json | 2 +- 80 files changed, 1414 insertions(+), 268 deletions(-) create mode 100644 schemas/api/PrivateChannel.broadcastEvent.schema.json create mode 100644 schemas/api/PrivateChannel.disconnectRequest.schema.json create mode 100644 schemas/api/PrivateChannel.disconnectResponse.schema.json create mode 100644 schemas/api/PrivateChannel.eventListenerAddedEvent.schema.json create mode 100644 schemas/api/PrivateChannel.eventListenerRemovedEvent.schema.json create mode 100644 schemas/api/PrivateChannel.onAddContextListenerEvent.schema.json create mode 100644 schemas/api/PrivateChannel.onDisconnectEvent.schema.json create mode 100644 schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json create mode 100644 schemas/api/addContextListenerRequest.schema.json create mode 100644 schemas/api/addContextListenerResponse.schema.json create mode 100644 schemas/api/addIntentListenerRequest.scheam.json create mode 100644 schemas/api/addIntentListenerResponse.schema.json create mode 100644 schemas/api/agentEventMessage.schema.json create mode 100644 schemas/api/agentResponseMessage.schema.json create mode 100644 schemas/api/appRequest.schema.json create mode 100644 schemas/api/broadcastRequest.schema.json create mode 100644 schemas/api/broadcastResponse.schema.json create mode 100644 schemas/api/channelChangedEvent..schema.json create mode 100644 schemas/api/common.schema.json create mode 100644 schemas/api/contextListenerUnsubscribeRequest.schema.json create mode 100644 schemas/api/contextListenerUnsubscribeResponse.schema.json create mode 100644 schemas/api/createPrivateChannelRequest.schema.json create mode 100644 schemas/api/createPrivateChannelResponse.schema.json create mode 100644 schemas/api/findInstancesRequest.schema.json create mode 100644 schemas/api/findInstancesResponse.schema.json create mode 100644 schemas/api/findIntentRequest.schema.json create mode 100644 schemas/api/findIntentResponse.schema.json create mode 100644 schemas/api/findIntentsByContextRequest.schema.json create mode 100644 schemas/api/findIntentsByContextResponse.schema.json create mode 100644 schemas/api/getAppMetadataRequest.schema.json create mode 100644 schemas/api/getAppMetadataResponse.schema.json create mode 100644 schemas/api/getCurrentChannelRequest.schema.json create mode 100644 schemas/api/getCurrentChannelResponse.schema.json create mode 100644 schemas/api/getInfoRequest.schema.json create mode 100644 schemas/api/getInfoResponse.schema.json create mode 100644 schemas/api/getOrCreateChannelRequest.schema.json create mode 100644 schemas/api/getOrCreateChannelResponse.schema.json create mode 100644 schemas/api/getUserChannelsRequest.schema.json create mode 100644 schemas/api/getUserChannelsResponse.schema.json create mode 100644 schemas/api/intentListenerUnsubscribeRequest.schema.json create mode 100644 schemas/api/intentListenerUnsubscribeResponse.schema.json create mode 100644 schemas/api/joinUserChannelRequest.schema.json create mode 100644 schemas/api/joinUserChannelsResponse.schema.json create mode 100644 schemas/api/leaveCurrentChannelRequest.schema.json create mode 100644 schemas/api/leaveCurrentChannelResponse.schema.json create mode 100644 schemas/api/openRequest.schema.json create mode 100644 schemas/api/openResponse.schema.json create mode 100644 schemas/api/raiseIntentForContextRequest.schema.json create mode 100644 schemas/api/raiseIntentForContextResponse.schema.json create mode 100644 schemas/api/raiseIntentRequest.schema.json create mode 100644 schemas/api/raiseIntentResponse.schema.json create mode 100644 schemas/api/raiseIntentResultResponse.schema.json diff --git a/schemas/api/PrivateChannel.broadcastEvent.schema.json b/schemas/api/PrivateChannel.broadcastEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.disconnectRequest.schema.json b/schemas/api/PrivateChannel.disconnectRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.disconnectResponse.schema.json b/schemas/api/PrivateChannel.disconnectResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.eventListenerAddedEvent.schema.json b/schemas/api/PrivateChannel.eventListenerAddedEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.eventListenerRemovedEvent.schema.json b/schemas/api/PrivateChannel.eventListenerRemovedEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.onAddContextListenerEvent.schema.json b/schemas/api/PrivateChannel.onAddContextListenerEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.onDisconnectEvent.schema.json b/schemas/api/PrivateChannel.onDisconnectEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json b/schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/README.md b/schemas/api/README.md index c782f2e7a..274b47798 100644 --- a/schemas/api/README.md +++ b/schemas/api/README.md @@ -1,10 +1,19 @@ # Intro -Quicktype, the chosen generation tool currently has some limitations that prevent fully automatic schema generation from the existing TS types. For example it can not handle interfaces that contain methods in their definition. It also fails to generate schemas even if a type contains unused references to other types or interfaces that contain async functions (Promise return types). Therefore, in order to generate the `api\schemas\api.schema.json` some manual intervention was needed. +The _schemas/api_ folder contains JSONSchema definitions that are used to implement wire protocols for an app working with a Desktop Agent, and for import into the Bridging wire protocols that shares many of the same structures. + +Please note: Quicktype, the chosen generation tool currently has some limitations that prevent fully automatic schema generation from the existing TS types. For example it can not handle interfaces that contain methods in their definition. It also fails to generate schemas even if a type contains unused references to other types or interfaces that contain async functions (Promise return types). Therefore, in order to generate the `api\schemas\api.schema.json` some manual intervention was needed. Once these limitations are not an issue the `api\schemas\t2sQuicktypeUtil.js` script should be moved to the root level of the project and a new npm script `"api-schema-gen": "node t2sQuicktypeUtil.js src/api schemas/api/api.schema.json"` should be added. -`api\schemas\api.schema.json` - partially auto-generated schema from the existing `src\api` types. -`api\schemas\baseImplementationMetadata.schema.json` - Used by bridging types that leave out the metadata of the calling application as it does not apply to bridging. -`api\schemas\intentResolution.schema.json` - At the moment it is not possible to auto-generate this due to limitations in the generation tool (quicktype) -`api\schemas\t2sQuicktypeUtil.js` - Script used to run the generation of the schema from the types. Should be moved to the root level of the repo once fully-automated generation can be achieved. +Contents: + +- `api\schemas\t2sQuicktypeUtil.js` - Script used to run the generation of the schema from the types. Should be moved to the root level of the repo once fully-automated generation can be achieved. +- `api\schemas\api.schema.json` - partially auto-generated schema from the existing `src\api` types and metadata objects. +- `api\schemas\common.schema.json` - common element definitions referenced in multiple other schemas in both the API and Bridging API protocols. +- `api\schemas\appRequest.schema.json` - The base message definition that requests from an app to the DA are derived from. +- `api\schemas\agentResponseMessage.schema.json` - The base message definition that API call response messages from a DA to an app are derived from. +- `api\schemas\agentEventMessage.schema.json` - The base message definition that event messages from a DA to an app are derived from. +- `api\schemas\*Request.schema.json` - Schemas defining request messages sent from apps to Desktop Agents. +- `api\schemas\*Response.schema.json` - Schemas defining responses from DAs to apps for request messages (sent from apps to Desktop Agents). +- `api\schemas\*Event.schema.json` - Schemas defining event messages sent from Desktop Agents to Apps. diff --git a/schemas/api/addContextListenerRequest.schema.json b/schemas/api/addContextListenerRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/addContextListenerResponse.schema.json b/schemas/api/addContextListenerResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/addIntentListenerRequest.scheam.json b/schemas/api/addIntentListenerRequest.scheam.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/addIntentListenerResponse.schema.json b/schemas/api/addIntentListenerResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/agentEventMessage.schema.json b/schemas/api/agentEventMessage.schema.json new file mode 100644 index 000000000..f75da99bd --- /dev/null +++ b/schemas/api/agentEventMessage.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/agentEventMessage.schema.json", + "title": "FDC3 Agent Event Message", + "type": "object", + "description": "A message from a Desktop Agent to an FDC3-enabled app representing an event.", + "properties": { + "type": { + "title": "Message Type", + "type": "string", + "enum": [ + "PrivateChannel.broadcastEvent", + "PrivateChannel.eventListenerAddedEvent", + "PrivateChannel.eventListenerRemovedEvent", + "PrivateChannel.onAddContextListenerEvent", + "PrivateChannel.onDisconnectEvent", + "PrivateChannel.onUnsubscribeEvent", + "channelChangedEvent" + ], + "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." + }, + "payload": { + "title": "Agent Event Message Payload", + "type": "object", + "description": "The message payload contains details of the event that the app is being notified about.", + "additionalProperties": true + }, + "meta": { + "title": "Agent Event Message Metadata", + "description": "Metadata for messages sent by a Desktop Agent to an App notifying it of an event.", + "type": "object", + "properties": { + "timestamp": { + "$ref": "common.schema.json#/$defs/Timestamp" + }, + "eventUuid": { + "$ref": "common.schema.json#/$defs/ResponseUuid" + } + }, + "required": ["timestamp", "eventUuid"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["type", "payload", "meta"] +} diff --git a/schemas/api/agentResponseMessage.schema.json b/schemas/api/agentResponseMessage.schema.json new file mode 100644 index 000000000..65e2057af --- /dev/null +++ b/schemas/api/agentResponseMessage.schema.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/agentResponseMessage.schema.json", + "title": "FDC3 Agent Response Message", + "type": "object", + "description": "A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the payload contains an `error` property, the request was unsuccessful.", + "properties": { + "type": { + "title": "Message Type", + "type": "string", + "enum": [ + "addContextListenerResponse", + "addIntentListenerResponse", + "broadcastResponse", + "createPrivateChannelResponse", + "findInstancesResponse", + "findIntentResponse", + "findIntentsByContextResponse", + "getAppMetadataResponse", + "getCurrentChannelResponse", + "getInfoResponse", + "getOrCreateChannelResponse", + "getUserChannelsResponse", + "joinUserChannelResponse", + "leaveCurrentChannelResponse", + "openResponse", + "raiseIntentResponse", + "raiseIntentForContextResponse", + "raiseIntentResultResponse", + "contextListenerUnsubscribeResponse", + "intentListenerUnsubscribeResponse", + "PrivateChannel.disconnectResponse" + ], + "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." + }, + "payload": { + "title": "Agent Response Message Payload", + "type": "object", + "description": "A payload for a response to an API call that will contain any return values or an `error` property containing a standardized error message indicating that the request was unsuccessful.", + "oneOf": [{ + "type": "object", + "properties": {}, + "additionalProperties": true + },{ + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/$defs/ErrorMessages" + } + }, + "required": ["success","error"], + "additionalProperties": false + }] + }, + "meta": { + "title": "Agent Response Message Metadata", + "description": "Metadata for messages sent by a Desktop Agent to an App in response to an API call", + "type": "object", + "properties": { + "timestamp": { + "$ref": "common.schema.json#/$defs/Timestamp" + }, + "requestUuid": { + "$ref": "common.schema.json#/$defs/ResponseUuid" + }, + "responseUuid": { + "$ref": "common.schema.json#/$defs/ResponseUuid" + }, + "source": { + "title": "Original Source AppIdentifier", + "description": "Field that represents the source application that the request being responded to was received from, for debugging purposes.", + "$ref": "api.schema.json#/definitions/AppIdentifier" + } + }, + "required": ["timestamp", "requestUuid", "responseUuid"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["type", "payload", "meta"] +} diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json new file mode 100644 index 000000000..068fe5579 --- /dev/null +++ b/schemas/api/appRequest.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/appRequest.schema.json", + "title": "FDC3 App Request Message", + "type": "object", + "description": "A request message from an FDC3-enabled app to a Desktop Agent.", + "properties": { + "type": { + "title": "Request Message type", + "type": "string", + "enum": [ + "addContextListenerRequest", + "addIntentListenerRequest", + "broadcastRequest", + "createPrivateChannelRequest", + "findInstancesRequest", + "findIntentRequest", + "findIntentsByContextRequest", + "getAppMetadataRequest", + "getCurrentChannelRequest", + "getInfoRequest", + "getOrCreateChannelRequest", + "getUserChannelsRequest", + "joinUserChannelRequest", + "leaveCurrentChannelRequest", + "openRequest", + "PrivateChannel.disconnectRequest", + "raiseIntentRequest", + "raiseIntentForContextRequest", + "contextListenerUnsubscribeRequest", + "intentListenerUnsubscribeRequest" + ], + "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Request' appended." + }, + "payload": { + "title": "Message payload", + "type": "object", + "description": "The message payload typically contains the arguments to FDC3 API functions." + }, + "meta": { + "title": "Request Metadata", + "description": "Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent.", + "type": "object", + "properties": { + "requestUuid": { + "$ref": "common.schema.json#/$defs/RequestUuid" + }, + "timestamp": { + "$ref": "common.schema.json#/$defs/Timestamp" + }, + "source": { + "title": "Source AppIdentifier", + "description": "Field that represents the source application that a request or response was received from. Please note that this may be set by an app or Desktop Agent proxy for debugging purposes but a Desktop Agent should make its own determination of the source of a message to avoid spoofing.", + "$ref": "api.schema.json#/definitions/AppIdentifier" + } + }, + "required": ["requestUuid", "timestamp"], + "additionalProperties": false + } + }, + "required": ["type", "payload", "meta"], + "additionalProperties": false +} diff --git a/schemas/api/broadcastRequest.schema.json b/schemas/api/broadcastRequest.schema.json new file mode 100644 index 000000000..f196c5ab9 --- /dev/null +++ b/schemas/api/broadcastRequest.schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/broadcastRequest.schema.json", + "type": "object", + "title": "Broadcast Request", + "description": "A request to broadcast context on a channel.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/BroadcastRequestType" + }, + "payload": { + "$ref": "#/$defs/BroadcastRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "BroadcastRequestType": { + "title": "Broadcast Request Message Type", + "const": "broadcastRequest" + }, + "BroadcastRequestPayload": { + "title": "broadcast Request Payload", + "type": "object", + "properties": { + "channelId": { + "type": "string", + "title": "Channel Id", + "description": "The Id of the Channel that the broadcast was sent on" + }, + "context": { + "$ref": "../context/context.schema.json", + "title": "Context", + "description": "The context object that was the payload of a broadcast message." + } + }, + "additionalProperties": false, + "required": [ + "channelId", + "context" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/broadcastResponse.schema.json b/schemas/api/broadcastResponse.schema.json new file mode 100644 index 000000000..3231a4da6 --- /dev/null +++ b/schemas/api/broadcastResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/broadcastResponse.schema.json", + "type": "object", + "title": "Broadcast Response", + "description": "A response to a request to broadcast context on a channel.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/BroadcastResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "BroadcastResponseType": { + "title": "Broadcast Response Message Type", + "const": "broadcastResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/channelChangedEvent..schema.json b/schemas/api/channelChangedEvent..schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json new file mode 100644 index 000000000..7aec93911 --- /dev/null +++ b/schemas/api/common.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/common.schema.json", + "title": "Common definitions", + "type": "object", + "description": "Common definitions that are referenced in the API and Bridging wire protocol schemas", + "$defs": { + "RequestUuid": { + "title": "Request UUID", + "type": "string", + "description": "Unique identifier for a request or event message. Required in all message types" + }, + "ResponseUuid": { + "title": "Response UUID", + "type": "string", + "description": "Unique identifier for a response to a specific message and must always be accompanied by a RequestUuid." + }, + "EventUuid": { + "title": "Event UUID", + "type": "string", + "description": "Unique identifier for an event message sent from a Desktop Agent to an app." + }, + "Timestamp": { + "title": "Timestamp", + "type": "string", + "format": "date-time", + "description": "Timestamp at which the message was generated" + }, + "ErrorMessages": { + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ChannelError" + }, + { + "$ref": "../api/api.schema.json#/definitions/OpenError" + }, + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/ResultError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + }, + "PrivateChannelEventListenerTypes": { + "title": "Private Channel Event Listener Types", + "description": "Event listener type names for Private Channel events", + "type": "string", + "enum": [ + "onAddContextListener", + "onUnsubscribe", + "onDisconnect" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/contextListenerUnsubscribeRequest.schema.json b/schemas/api/contextListenerUnsubscribeRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/contextListenerUnsubscribeResponse.schema.json b/schemas/api/contextListenerUnsubscribeResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/createPrivateChannelRequest.schema.json b/schemas/api/createPrivateChannelRequest.schema.json new file mode 100644 index 000000000..f64a9c6b5 --- /dev/null +++ b/schemas/api/createPrivateChannelRequest.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/createPrivateChannelRequest.schema.json", + "type": "object", + "title": "CreatePrivateChannel Request", + "description": "Request to return a Channel with an auto-generated identity that is intended for private communication between applications.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/CreatePrivateChannelRequestType" + }, + "payload": { + "$ref": "#/$defs/CreatePrivateChannelRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "CreatePrivateChannelRequestType": { + "title": "CreatePrivateChannel Request Message Type", + "const": "createPrivateChannelRequest" + }, + "CreatePrivateChannelRequestPayload": { + "title": "CreatePrivateChannel Request Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/createPrivateChannelResponse.schema.json b/schemas/api/createPrivateChannelResponse.schema.json new file mode 100644 index 000000000..ee26d6eeb --- /dev/null +++ b/schemas/api/createPrivateChannelResponse.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/createPrivateChannelResponse.schema.json", + "type": "object", + "title": "CreatePrivateChannel Response", + "description": "A response to a createPrivateChannel request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/CreatePrivateChannelResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/CreatePrivateChannelSuccessResponsePayload" + }, + { + "$ref": "#/$defs/CreatePrivateChannelErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "CreatePrivateChannelResponseType": { + "title": "CreatePrivateChannel Response Message Type", + "const": "createPrivateChannelResponse" + }, + "CreatePrivateChannelSuccessResponsePayload": { + "title": "CreatePrivateChannel Response Payload", + "type": "object", + "properties": { + "privateChannel": { + "$ref": "../api/api.schema.json#/definitions/Channel" + } + }, + "required": [ + "privateChannel" + ], + "additionalProperties": false + }, + "CreatePrivateChannelErrorResponsePayload": { + "title": "CreatePrivateChannel Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findInstancesRequest.schema.json b/schemas/api/findInstancesRequest.schema.json new file mode 100644 index 000000000..3a7247057 --- /dev/null +++ b/schemas/api/findInstancesRequest.schema.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findInstancesRequest.schema.json", + "type": "object", + "title": "FindInstances Request", + "description": "A request for details of instances of a particular app.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindInstancesRequestType" + }, + "payload": { + "$ref": "#/$defs/FindInstancesRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindInstancesRequestType": { + "title": "FindInstances Request Message Type", + "const": "findInstancesRequest" + }, + "FindInstancesRequestPayload": { + "type": "object", + "title": "FindInstances Request Payload", + "properties": { + "app": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": ["app"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findInstancesResponse.schema.json b/schemas/api/findInstancesResponse.schema.json new file mode 100644 index 000000000..61758dba4 --- /dev/null +++ b/schemas/api/findInstancesResponse.schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findInstancesResponse.schema.json", + "type": "object", + "title": "FindInstances Response", + "description": "A response to a findInstances request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindInstancesResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "$defs/FindInstancesSuccessResponsePayload" + }, + { + "$ref": "$defs/FindInstancesErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindInstancesResponseType": { + "title": "FindInstances Response Message Type", + "const": "findInstancesResponse" + }, + "FindInstancesSuccessResponsePayload": { + "title": "Agent Response Message Payload", + "type": "object", + "description": "The message payload contains a flag indicating whether the API call was successful, plus any return values for the FDC3 API function called, or indicating that the request resulted in an error and including a standardized error message.", + "properties": { + "appIdentifiers": { + "type": "array", + "items": { + "$ref": "../api/api.schema.json#/definitions/AppMetadata" + } + } + }, + "required": [ + "appIdentifiers" + ], + "additionalProperties": false + }, + "FindInstancesErrorResponsePayload": { + "title": "", + "type": "object", + "properties": { + "error": { + "title": "findInstances Errors", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findIntentRequest.schema.json b/schemas/api/findIntentRequest.schema.json new file mode 100644 index 000000000..88e06ec13 --- /dev/null +++ b/schemas/api/findIntentRequest.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findIntentRequest.schema.json", + "type": "object", + "title": "FindIntent Request", + "description": "A request for details of apps available to resolve a particular intent and context pair.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindIntentRequestType" + }, + "payload": { + "$ref": "#/$defs/FindIntentRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindIntentRequestType": { + "title": "FindIntent Request Message Type", + "const": "findIntentRequest" + }, + "FindIntentRequestPayload": { + "title": "FindIntent Request Payload", + "type": "object", + "properties": { + "intent": { + "title": "Intent name", + "type": "string" + }, + "context": { + "title": "Context argument", + "$ref": "../context/context.schema.json" + }, + "resultType": { + "title": "Result type argument", + "type": "string" + }, + "required": [ + "intent" + ] + }, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findIntentResponse.schema.json b/schemas/api/findIntentResponse.schema.json new file mode 100644 index 000000000..19a73b7b1 --- /dev/null +++ b/schemas/api/findIntentResponse.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findIntentResponse.schema.json", + "type": "object", + "title": "FindIntent Response", + "description": "A response to a findIntent request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindIntentResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/FindIntentSuccessResponsePayload" + }, + { + "$ref": "#/$defs/FindIntentErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindIntentResponseType": { + "title": "FindIntent Response Message Type", + "const": "findIntentResponse" + }, + "FindIntentSuccessResponsePayload": { + "title": "FindIntent Response Payload", + "type": "object", + "properties": { + "appIntent": { + "$ref": "../api/api.schema.json#/definitions/AppIntent" + } + }, + "required": [ + "appIntent" + ] + }, + "FindIntentErrorResponsePayload": { + "title": "", + "type": "object", + "properties": { + "error": { + "title": "findIntent Errors", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findIntentsByContextRequest.schema.json b/schemas/api/findIntentsByContextRequest.schema.json new file mode 100644 index 000000000..5b43d0639 --- /dev/null +++ b/schemas/api/findIntentsByContextRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findIntentsByContextRequest.schema.json", + "type": "object", + "title": "FindIntentsByContext Request", + "description": "A request for details of intents and apps available to resolve them for a particular context.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindIntentsByContextRequestType" + }, + "payload": { + "$ref": "#/$defs/FindIntentsByContextRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindIntentsByContextRequestType": { + "title": "FindIntentsByContext Request Message Type", + "const": "findIntentsByContextRequest" + }, + "FindIntentsByContextRequestPayload": { + "title": "FindIntentsByContext Request Payload", + "type": "object", + "properties": { + "context": { + "$ref": "../context/context.schema.json" + } + }, + "required": [ + "context" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json new file mode 100644 index 000000000..0d4162128 --- /dev/null +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json", + "type": "object", + "title": "FindIntentsByContextsByContext Response", + "description": "A response to a findIntentsByContext request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/FindIntentsByContextResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/FindIntentsByContextSuccessResponsePayload" + }, + { + "$ref": "#/$defs/FindIntentsByContextErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "FindIntentsByContextResponseType": { + "title": "FindIntentsByContext Response Message Type", + "const": "findIntentsByContextResponse" + }, + "FindIntentsByContextSuccessResponsePayload": { + "title": "FindIntentsByContext Response Payload", + "type": "object", + "properties": { + "appIntents": { + "type": "array", + "items": { + "$ref": "../api/api.schema.json#/definitions/AppIntent" + }, + "additionalProperties": false + } + }, + "required": [ + "appIntents" + ] + }, + "FindIntentsByContextErrorResponsePayload": { + "title": "FindIntentsByContext Error Response Payload", + "type": "object", + "properties": { + "error": { + "title": "FindIntentsByContext Error Message", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getAppMetadataRequest.schema.json b/schemas/api/getAppMetadataRequest.schema.json new file mode 100644 index 000000000..d670335c6 --- /dev/null +++ b/schemas/api/getAppMetadataRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getAppMetadataRequest.schema.json", + "type": "object", + "title": "GetAppMetadata Request", + "description": "A request for metadata about an app.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetAppMetadataRequestType" + }, + "payload": { + "$ref": "#/$defs/GetAppMetadataRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetAppMetadataRequestType": { + "title": "GetAppMetadata Request Message Type", + "const": "getAppMetadataRequest" + }, + "GetAppMetadataRequestPayload": { + "title": "GetAppMetadata Request Payload", + "type": "object", + "properties": { + "app": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": [ + "app" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getAppMetadataResponse.schema.json b/schemas/api/getAppMetadataResponse.schema.json new file mode 100644 index 000000000..7aeceba35 --- /dev/null +++ b/schemas/api/getAppMetadataResponse.schema.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getAppMetadataResponse.schema.json", + "type": "object", + "title": "GetAppMetadata Response", + "description": "A response to a getAppMetadata request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetAppMetadataResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetAppMetadataSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetAppMetadataErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetAppMetadataResponseType": { + "title": "GetAppMetadata Response Message Type", + "const": "getAppMetadataResponse" + }, + "GetAppMetadataSuccessResponsePayload": { + "title": "GetAppMetadata Response Payload", + "type": "object", + "properties": { + "appMetadata": { + "$ref": "../api/api.schema.json#/definitions/AppMetadata" + } + }, + "required": [ + "appMetadata" + ], + "additionalProperties": false + }, + "GetAppMetadataErrorResponsePayload": { + "title": "GetAppMetadata Error Response Payload", + "type": "object", + "properties": { + "error": { + "title": "GetAppMetadata Error Message", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getCurrentChannelRequest.schema.json b/schemas/api/getCurrentChannelRequest.schema.json new file mode 100644 index 000000000..bf8a803d0 --- /dev/null +++ b/schemas/api/getCurrentChannelRequest.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getCurrentChannelRequest.schema.json", + "type": "object", + "title": "GetCurrentChannel Request", + "description": "A request to return the Channel object for the current User channel membership. Returns `null` if the app is not joined to a channel.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetCurrentChannelRequestType" + }, + "payload": { + "$ref": "#/$defs/GetCurrentChannelRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetCurrentChannelRequestType": { + "title": "GetCurrentChannel Request Message Type", + "const": "getCurrentChannelRequest" + }, + "GetCurrentChannelRequestPayload": { + "title": "GetCurrentChannel Request Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getCurrentChannelResponse.schema.json b/schemas/api/getCurrentChannelResponse.schema.json new file mode 100644 index 000000000..87714a122 --- /dev/null +++ b/schemas/api/getCurrentChannelResponse.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getCurrentChannelResponse.schema.json", + "type": "object", + "title": "GetCurrentChannel Response", + "description": "A response to a getCurrentChannel request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetCurrentChannelResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetCurrentChannelSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetCurrentChannelErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetCurrentChannelResponseType": { + "title": "GetCurrentChannel Response Message Type", + "const": "getCurrentChannelResponse" + }, + "GetCurrentChannelSuccessResponsePayload": { + "title": "GetCurrentChannel Response Payload", + "type": "object", + "properties": { + "channel": { + "oneOf": [ + { "$ref": "../api/api.schema.json#/definitions/Channel" }, + { "type": "null" } + ] + } + }, + "required": [ + "channel" + ], + "additionalProperties": false + }, + "GetCurrentChannelErrorResponsePayload": { + "title": "GetCurrentChannel Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/$defs/ErrorMessages" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getInfoRequest.schema.json b/schemas/api/getInfoRequest.schema.json new file mode 100644 index 000000000..b200b0ee0 --- /dev/null +++ b/schemas/api/getInfoRequest.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getInfoRequest.schema.json", + "type": "object", + "title": "GetInfo Request", + "description": "Request to retrieve information about the FDC3 Desktop Agent implementation and the metadata of the calling application according to the desktop agent.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetInfoRequestType" + }, + "payload": { + "$ref": "#/$defs/GetInfoRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetInfoRequestType": { + "title": "GetInfo Request Message Type", + "const": "getInfoRequest" + }, + "GetInfoRequestPayload": { + "title": "GetInfo Request Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getInfoResponse.schema.json b/schemas/api/getInfoResponse.schema.json new file mode 100644 index 000000000..bf44798a1 --- /dev/null +++ b/schemas/api/getInfoResponse.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getInfoResponse.schema.json", + "type": "object", + "title": "GetInfo Response", + "description": "A response to a getInfo request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetInfoResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetInfoSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetInfoErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetInfoResponseType": { + "title": "GetInfo Response Message Type", + "const": "getInfoResponse" + }, + "GetInfoSuccessResponsePayload": { + "title": "GetInfo Response Payload", + "type": "object", + "properties": { + "implementationMetadata": { + "$ref": "api.schema.json#/definitions/ImplementationMetadata" + } + }, + "required": [ + "implementationMetadata" + ], + "additionalProperties": false + }, + "GetInfoErrorResponsePayload": { + "title": "GetInfo Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/$defs/ErrorMessages" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getOrCreateChannelRequest.schema.json b/schemas/api/getOrCreateChannelRequest.schema.json new file mode 100644 index 000000000..9b6354d87 --- /dev/null +++ b/schemas/api/getOrCreateChannelRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getOrCreateChannelRequest.schema.json", + "type": "object", + "title": "GetOrCreateChannel Request", + "description": "Request to return a Channel with an auto-generated identity that is intended for private communication between applications.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetOrCreateChannelRequestType" + }, + "payload": { + "$ref": "#/$defs/GetOrCreateChannelRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetOrCreateChannelRequestType": { + "title": "GetOrCreateChannel Request Message Type", + "const": "getOrCreateChannelRequest" + }, + "GetOrCreateChannelRequestPayload": { + "title": "GetOrCreateChannel Request Payload", + "type": "object", + "properties": { + "channelId": { + "title": "Channel Id", + "description": "The id of the channel to return", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["channelId"] + } + } +} \ No newline at end of file diff --git a/schemas/api/getOrCreateChannelResponse.schema.json b/schemas/api/getOrCreateChannelResponse.schema.json new file mode 100644 index 000000000..4ab59df7e --- /dev/null +++ b/schemas/api/getOrCreateChannelResponse.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getOrCreateChannelResponse.schema.json", + "type": "object", + "title": "GetOrCreateChannel Response", + "description": "A response to a getOrCreateChannel request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetOrCreateChannelResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetOrCreateChannelSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetOrCreateChannelErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetOrCreateChannelResponseType": { + "title": "GetOrCreateChannel Response Message Type", + "const": "getOrCreateChannelResponse" + }, + "GetOrCreateChannelSuccessResponsePayload": { + "title": "GetOrCreateChannel Response Payload", + "type": "object", + "properties": { + "channel": { + "$ref": "../api/api.schema.json#/definitions/Channel" + } + }, + "required": [ + "channel" + ], + "additionalProperties": false + }, + "GetOrCreateChannelErrorResponsePayload": { + "title": "GetOrCreateChannel Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getUserChannelsRequest.schema.json b/schemas/api/getUserChannelsRequest.schema.json new file mode 100644 index 000000000..37529270d --- /dev/null +++ b/schemas/api/getUserChannelsRequest.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getUserChannelsRequest.schema.json", + "type": "object", + "title": "GetUserChannels Request", + "description": "Request to return a Channel with an auto-generated identity that is intended for private communication between applications.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetUserChannelsRequestType" + }, + "payload": { + "$ref": "#/$defs/GetUserChannelsRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetUserChannelsRequestType": { + "title": "GetUserChannels Request Message Type", + "const": "getUserChannelsRequest" + }, + "GetUserChannelsRequestPayload": { + "title": "GetUserChannels Request Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getUserChannelsResponse.schema.json b/schemas/api/getUserChannelsResponse.schema.json new file mode 100644 index 000000000..76bcce75c --- /dev/null +++ b/schemas/api/getUserChannelsResponse.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getUserChannelsResponse.schema.json", + "type": "object", + "title": "GetUserChannels Response", + "description": "A response to a getUserChannels request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetUserChannelsResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetUserChannelsSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetUserChannelsErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetUserChannelsResponseType": { + "title": "GetUserChannels Response Message Type", + "const": "getUserChannelsResponse" + }, + "GetUserChannelsSuccessResponsePayload": { + "title": "GetUserChannels Response Payload", + "type": "object", + "properties": { + "userChannels": { + "type": "array", + "items": { + "$ref": "../api/api.schema.json#/definitions/Channel" + } + } + }, + "required": [ + "userChannels" + ], + "additionalProperties": false + }, + "GetUserChannelsErrorResponsePayload": { + "title": "GetUserChannels Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/intentListenerUnsubscribeRequest.schema.json b/schemas/api/intentListenerUnsubscribeRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/intentListenerUnsubscribeResponse.schema.json b/schemas/api/intentListenerUnsubscribeResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/joinUserChannelRequest.schema.json b/schemas/api/joinUserChannelRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/joinUserChannelsResponse.schema.json b/schemas/api/joinUserChannelsResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/leaveCurrentChannelRequest.schema.json b/schemas/api/leaveCurrentChannelRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/leaveCurrentChannelResponse.schema.json b/schemas/api/leaveCurrentChannelResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/openRequest.schema.json b/schemas/api/openRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/openResponse.schema.json b/schemas/api/openResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/raiseIntentForContextRequest.schema.json b/schemas/api/raiseIntentForContextRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/raiseIntentRequest.schema.json b/schemas/api/raiseIntentRequest.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json new file mode 100644 index 000000000..e69de29bb diff --git a/schemas/bridging/agentErrorResponse.schema.json b/schemas/bridging/agentErrorResponse.schema.json index 75a0051c8..67b22bc11 100644 --- a/schemas/bridging/agentErrorResponse.schema.json +++ b/schemas/bridging/agentErrorResponse.schema.json @@ -25,7 +25,7 @@ "description": "Error message payload containing an standardized error string.", "properties": { "error": { - "$ref": "common.schema.json#/$defs/ErrorMessages" + "$ref": "../api/common.schema.json#/$defs/ErrorMessages" } }, "unevaluatedProperties": false, diff --git a/schemas/bridging/agentRequest.schema.json b/schemas/bridging/agentRequest.schema.json index f9f3ad553..edf763ef9 100644 --- a/schemas/bridging/agentRequest.schema.json +++ b/schemas/bridging/agentRequest.schema.json @@ -43,10 +43,10 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" }, "source": { "title": "Source identifier", diff --git a/schemas/bridging/agentResponse.schema.json b/schemas/bridging/agentResponse.schema.json index 8c795b59e..e2d1d3a73 100644 --- a/schemas/bridging/agentResponse.schema.json +++ b/schemas/bridging/agentResponse.schema.json @@ -37,13 +37,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" } }, "required": ["requestUuid", "responseUuid", "timestamp"], diff --git a/schemas/bridging/bridgeErrorResponse.schema.json b/schemas/bridging/bridgeErrorResponse.schema.json index bd6fd15c0..31a55eb52 100644 --- a/schemas/bridging/bridgeErrorResponse.schema.json +++ b/schemas/bridging/bridgeErrorResponse.schema.json @@ -16,7 +16,7 @@ "description": "The error message payload contains details of an error return to the app or agent that raised the original request.", "properties": { "error": { - "$ref": "common.schema.json#/$defs/ErrorMessages" + "$ref": "../api/common.schema.json#/$defs/ErrorMessages" } } }, @@ -33,13 +33,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" }, "errorSources": { "$ref": "common.schema.json#/$defs/BridgeResponseErrorSources" diff --git a/schemas/bridging/bridgeRequest.schema.json b/schemas/bridging/bridgeRequest.schema.json index 27d50f8d8..cb4bf0e6d 100644 --- a/schemas/bridging/bridgeRequest.schema.json +++ b/schemas/bridging/bridgeRequest.schema.json @@ -28,10 +28,10 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" }, "source": { "title": "Bridge Source identifier", diff --git a/schemas/bridging/bridgeResponse.schema.json b/schemas/bridging/bridgeResponse.schema.json index 0e3a23bdf..b05c84fa1 100644 --- a/schemas/bridging/bridgeResponse.schema.json +++ b/schemas/bridging/bridgeResponse.schema.json @@ -28,13 +28,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" }, "sources": { "$ref": "common.schema.json#/$defs/BridgeResponseSources" diff --git a/schemas/bridging/broadcastAgentRequest.schema.json b/schemas/bridging/broadcastAgentRequest.schema.json index fb95b4fea..24aedc190 100644 --- a/schemas/bridging/broadcastAgentRequest.schema.json +++ b/schemas/bridging/broadcastAgentRequest.schema.json @@ -18,26 +18,10 @@ "description": "A request to broadcast context on a channel.", "properties": { "type": { - "title": "Broadcast Request Message Type", - "const": "broadcastRequest" + "$ref": "../api/broadcastRequest.schema.json#/$defs/BroadcastRequestType" }, "payload": { - "title": "broadcast Request Payload", - "type": "object", - "properties": { - "channelId": { - "type": "string", - "title": "Channel Id", - "description": "The Id of the Channel that the broadcast was sent on" - }, - "context": { - "$ref": "../context/context.schema.json", - "title": "Context", - "description": "The context object that was the payload of a broadcast message." - } - }, - "additionalProperties": false, - "required": ["channelId", "context"] + "$ref": "../api/broadcastRequest.schema.json#/$defs/BroadcastRequestPayload" }, "meta": { "type": "object", diff --git a/schemas/bridging/common.schema.json b/schemas/bridging/common.schema.json index edbcd2858..3420ff166 100644 --- a/schemas/bridging/common.schema.json +++ b/schemas/bridging/common.schema.json @@ -1,26 +1,10 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/bridging/common.schema.json", - "title": "Bridging Commons", + "$id": "https://fdc3.finos.org/schemas/next/api/common.schema.json", + "title": "Bridge Common definitions", "type": "object", - "description": "Common elements referenced by other schemas", + "description": "Common definitions that are referenced only in the Bridging wire protocol schemas", "$defs": { - "RequestUuid": { - "title": "Request UUID", - "type": "string", - "description": "UUID for the request" - }, - "ResponseUuid": { - "title": "Response UUID", - "type": "string", - "description": "UUID for this specific response message." - }, - "Timestamp": { - "title": "Timestamp", - "type": "string", - "format": "date-time", - "description": "Timestamp at which request was generated" - }, "RequestSource": { "title": "Source identifier", "description": "Field that represents the source application that a request or response was received from, or the source Desktop Agent if it issued the request or response itself.", @@ -94,25 +78,6 @@ ], "description": "Array of DesktopAgentIdentifiers for responses that were not returned to the bridge before the timeout or because an error occurred. May be omitted if all sources responded without errors. MUST include the `desktopAgent` field when returned by the bridge." }, - "ErrorMessages": { - "oneOf": [ - { - "$ref": "../api/api.schema.json#/definitions/ChannelError" - }, - { - "$ref": "../api/api.schema.json#/definitions/OpenError" - }, - { - "$ref": "../api/api.schema.json#/definitions/ResolveError" - }, - { - "$ref": "../api/api.schema.json#/definitions/ResultError" - }, - { - "$ref": "../api/api.schema.json#/definitions/BridgingError" - } - ] - }, "BridgeResponseErrorDetails": { "title": "Response Error Details", "type": "array", @@ -121,16 +86,6 @@ }, "description": "Array of error message strings for responses that were not returned to the bridge before the timeout or because an error occurred. Should be the same length as the `errorSources` array and ordered the same. May be omitted if all sources responded without errors." }, - "PrivateChannelEventListenerTypes": { - "title": "Private Channel Event Listener Types", - "description": "Event listener type names for Private Channel events", - "type": "string", - "enum": [ - "onAddContextListener", - "onUnsubscribe", - "onDisconnect" - ] - }, "DesktopAgentImplementationMetadata": { "description": "Includes the name assigned to the Desktop Agent by the Bridge.", "title": "DesktopAgentImplementationMetadata", diff --git a/schemas/bridging/connectionStep.schema.json b/schemas/bridging/connectionStep.schema.json index 3c3a377b0..03d558571 100644 --- a/schemas/bridging/connectionStep.schema.json +++ b/schemas/bridging/connectionStep.schema.json @@ -35,13 +35,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" } }, "required": ["timestamp"], diff --git a/schemas/bridging/connectionStep2Hello.schema.json b/schemas/bridging/connectionStep2Hello.schema.json index 0ac240f6c..82ada01e5 100644 --- a/schemas/bridging/connectionStep2Hello.schema.json +++ b/schemas/bridging/connectionStep2Hello.schema.json @@ -57,7 +57,7 @@ "type": "object", "properties": { "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" } }, "additionalProperties": false, diff --git a/schemas/bridging/connectionStep3Handshake.schema.json b/schemas/bridging/connectionStep3Handshake.schema.json index 11c683d12..408a60f84 100644 --- a/schemas/bridging/connectionStep3Handshake.schema.json +++ b/schemas/bridging/connectionStep3Handshake.schema.json @@ -81,10 +81,10 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" } }, "additionalProperties": false, diff --git a/schemas/bridging/connectionStep4AuthenticationFailed.schema.json b/schemas/bridging/connectionStep4AuthenticationFailed.schema.json index 6cae52eca..20b03d0c0 100644 --- a/schemas/bridging/connectionStep4AuthenticationFailed.schema.json +++ b/schemas/bridging/connectionStep4AuthenticationFailed.schema.json @@ -37,13 +37,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" } }, "additionalProperties": false, diff --git a/schemas/bridging/connectionStep6ConnectedAgentsUpdate.schema.json b/schemas/bridging/connectionStep6ConnectedAgentsUpdate.schema.json index df8782dd0..5693ec015 100644 --- a/schemas/bridging/connectionStep6ConnectedAgentsUpdate.schema.json +++ b/schemas/bridging/connectionStep6ConnectedAgentsUpdate.schema.json @@ -63,13 +63,13 @@ "type": "object", "properties": { "requestUuid": { - "$ref": "common.schema.json#/$defs/RequestUuid" + "$ref": "../api/common.schema.json#/$defs/RequestUuid" }, "responseUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "../api/common.schema.json#/$defs/ResponseUuid" }, "timestamp": { - "$ref": "common.schema.json#/$defs/Timestamp" + "$ref": "../api/common.schema.json#/$defs/Timestamp" } }, "additionalProperties": false, diff --git a/schemas/bridging/findInstancesAgentErrorResponse.schema.json b/schemas/bridging/findInstancesAgentErrorResponse.schema.json index 798840f59..b703c106b 100644 --- a/schemas/bridging/findInstancesAgentErrorResponse.schema.json +++ b/schemas/bridging/findInstancesAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a findInstances request that contains an error.", "properties": { "type": { - "title": "FindInstances Response Message Type", - "const": "findInstancesResponse" + "$ref": "../api/findInstancesResponse.schema.json#/$defs/FindInstancesResponseType" }, "payload": { - "title": "FindInstances Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "FindInstances Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/ResolveError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "required": ["error"], - "additionalProperties": false + "$ref": "../api/findInstancesResponse.schema.json#/$defs/FindInstancesErrorResponsePayload" }, "meta": { "title": "FindInstances Response Metadata", diff --git a/schemas/bridging/findInstancesAgentRequest.schema.json b/schemas/bridging/findInstancesAgentRequest.schema.json index 3ea08288f..4d9d1934a 100644 --- a/schemas/bridging/findInstancesAgentRequest.schema.json +++ b/schemas/bridging/findInstancesAgentRequest.schema.json @@ -18,19 +18,10 @@ "description": "A request for details of instances of a particular app", "properties":{ "type": { - "title": "FindInstances Request Message Type", - "const": "findInstancesRequest" + "$ref": "../api/findInstancesRequest.schema.json#/$defs/FindInstancesRequestType" }, "payload": { - "type": "object", - "title": "FindInstances Request Payload", - "properties": { - "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" - } - }, - "required": ["app"], - "additionalProperties": false + "$ref": "../api/findInstancesRequest.schema.json#/$defs/FindInstancesRequestPayload" }, "meta": { "title": "FindInstances request metadata", diff --git a/schemas/bridging/findInstancesAgentResponse.schema.json b/schemas/bridging/findInstancesAgentResponse.schema.json index 67f3937c6..87518e93f 100644 --- a/schemas/bridging/findInstancesAgentResponse.schema.json +++ b/schemas/bridging/findInstancesAgentResponse.schema.json @@ -18,29 +18,14 @@ "description": "A response to a findInstances request.", "properties": { "type": { - "title": "FindInstances Response Message Type", - "const": "findInstancesResponse" + "$ref": "../api/findInstancesResponse.schema.json#/$defs/FindInstancesResponseType" }, "payload": { - "title": "FindInstances Response Payload", - "type": "object", - "properties": { - "appIdentifiers": { - "type": "array", - "items": { - "$ref": "../api/api.schema.json#/definitions/AppMetadata" - } - } - }, - "required": ["appIdentifiers"], - "additionalProperties": false + "$ref": "../api/findInstancesResponse.schema.json#/$defs/FindInstancesSuccessResponsePayload" }, - "meta": { - "title": "FindInstances Response Metadata", - "type": "object" - } + "meta": true }, "additionalProperties": false } } -} +} \ No newline at end of file diff --git a/schemas/bridging/findIntentAgentErrorResponse.schema.json b/schemas/bridging/findIntentAgentErrorResponse.schema.json index b8d89cd9b..7c2e3e895 100644 --- a/schemas/bridging/findIntentAgentErrorResponse.schema.json +++ b/schemas/bridging/findIntentAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a findIntent request that contains an error.", "properties": { "type": { - "title": "FindIntent Response Message Type", - "const": "findIntentResponse" + "$ref": "../api/findIntentResponse.schema.json#/$defs/FindIntentResponseType" }, "payload": { - "title": "FindIntent Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "FindIntent Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/ResolveError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "required": ["error"], - "additionalProperties": false + "$ref": "../api/findIntentResponse.schema.json#/$defs/FindIntentErrorResponsePayload" }, "meta": { "title": "FindIntent Response Metadata", diff --git a/schemas/bridging/findIntentAgentRequest.schema.json b/schemas/bridging/findIntentAgentRequest.schema.json index 59c3dd70e..770b3ef71 100644 --- a/schemas/bridging/findIntentAgentRequest.schema.json +++ b/schemas/bridging/findIntentAgentRequest.schema.json @@ -18,28 +18,10 @@ "description": "A request for details of apps available to resolve a particular intent and context pair.", "properties": { "type": { - "title": "FindIntent Request Message Type", - "const": "findIntentRequest" + "$ref": "../api/findIntentRequest.schema.json#/$defs/FindIntentRequestType" }, "payload": { - "title": "FindIntent Request Payload", - "type": "object", - "properties": { - "intent": { - "title": "Intent name", - "type": "string" - }, - "context": { - "title": "Context argument", - "$ref": "../context/context.schema.json" - }, - "resultType": { - "title": "Result type argument", - "type": "string" - } - }, - "required": ["intent"], - "additionalProperties": false + "$ref": "../api/findIntentRequest.schema.json#/$defs/FindIntentRequestPayload" }, "meta": { "title" : "FindIntent Request Metadata", diff --git a/schemas/bridging/findIntentAgentResponse.schema.json b/schemas/bridging/findIntentAgentResponse.schema.json index 8969469bf..9f3b4685d 100644 --- a/schemas/bridging/findIntentAgentResponse.schema.json +++ b/schemas/bridging/findIntentAgentResponse.schema.json @@ -18,19 +18,10 @@ "description": "A response to a findIntent request.", "properties": { "type": { - "title": "FindIntent Response Message Type", - "const": "findIntentResponse" + "$ref": "../api/findIntentResponse.schema.json#/$defs/FindIntentResponseType" }, "payload": { - "title": "FindIntent Response Payload", - "type": "object", - "properties": { - "appIntent": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" - } - }, - "required": ["appIntent"], - "additionalProperties": false + "$ref": "../api/findIntentResponse.schema.json#/$defs/FindIntentSuccessResponsePayload" }, "meta": { "title": "FindIntent Response Metadata", diff --git a/schemas/bridging/findIntentsByContextAgentErrorResponse.schema.json b/schemas/bridging/findIntentsByContextAgentErrorResponse.schema.json index 0bd1646c5..8270d0355 100644 --- a/schemas/bridging/findIntentsByContextAgentErrorResponse.schema.json +++ b/schemas/bridging/findIntentsByContextAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a findIntentsByContext request that contains an error.", "properties": { "type": { - "title": "FindIntentsByContext Response Message Type", - "const": "findIntentsByContextResponse" + "$ref": "../api/findIntentsByContextResponse.schema.json#/$defs/FindIntentsByContextResponseType" }, "payload": { - "title": "FindIntentsByContext Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "FindIntentsByContext Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/ResolveError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "additionalProperties": false, - "required": ["error"] + "$ref": "../api/findIntentsByContextResponse.schema.json#/$defs/FindIntentsByContextErrorResponsePayload" }, "meta": { "title": "FindIntentsByContext Response Metadata", diff --git a/schemas/bridging/findIntentsByContextAgentRequest.schema.json b/schemas/bridging/findIntentsByContextAgentRequest.schema.json index 9f1bacf86..5dacbb41f 100644 --- a/schemas/bridging/findIntentsByContextAgentRequest.schema.json +++ b/schemas/bridging/findIntentsByContextAgentRequest.schema.json @@ -18,19 +18,10 @@ "description": "A request for details of intents and apps available to resolve them for a particular context.", "properties": { "type": { - "title": "FindIntentsByContext Request Message Type", - "const": "findIntentsByContextRequest" + "$ref": "../api/findIntentsByContextRequest.schema.json#/$defs/FindIntentsByContextRequestType" }, "payload": { - "title": "FindIntentsByContext Request Payload", - "type": "object", - "properties": { - "context": { - "$ref": "../context/context.schema.json" - } - }, - "required": ["context"], - "additionalProperties": false + "$ref": "../api/findIntentsByContextRequest.schema.json#/$defs/FindIntentsByContextRequestPayload" }, "meta": { "title": "FindIntentsByContext Request Metadata", diff --git a/schemas/bridging/findIntentsByContextAgentResponse.schema.json b/schemas/bridging/findIntentsByContextAgentResponse.schema.json index 7f909d419..99f5f04a1 100644 --- a/schemas/bridging/findIntentsByContextAgentResponse.schema.json +++ b/schemas/bridging/findIntentsByContextAgentResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a findIntentsByContext request.", "properties": { "type": { - "title": "FindIntentsByContext Response Message Type", - "const": "findIntentsByContextResponse" + "$ref": "../api/findIntentsByContextResponse.schema.json#/$defs/FindIntentsByContextResponseType" }, "payload": { - "title": "FindIntentsByContext Response Payload", - "type": "object", - "properties": { - "appIntents": { - "type": "array", - "items": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "required": ["appIntents"] + "$ref": "../api/findIntentsByContextResponse.schema.json#/$defs/FindIntentsByContextSuccessResponsePayload" }, "meta": { "title": "FindIntentsByContext Response Metadata", diff --git a/schemas/bridging/getAppMetadataAgentErrorResponse.schema.json b/schemas/bridging/getAppMetadataAgentErrorResponse.schema.json index 57cad8b2c..03cb59ab0 100644 --- a/schemas/bridging/getAppMetadataAgentErrorResponse.schema.json +++ b/schemas/bridging/getAppMetadataAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a getAppMetadata request that contains an error.", "properties": { "type": { - "title": "GetAppMetadata Response Message Type", - "const": "getAppMetadataResponse" + "$ref": "../api/getAppMetadataResponse.schema.json#/$defs/GetAppMetadataResponseType" }, "payload": { - "title": "GetAppMetadata Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "GetAppMetadata Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/ResolveError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "required": ["error"], - "additionalProperties": false + "$ref": "../api/getAppMetadataResponse.schema.json#/$defs/GetAppMetadataErrorResponsePayload" }, "meta": { "title": "GetAppMetadata Response Metadata", diff --git a/schemas/bridging/getAppMetadataAgentRequest.schema.json b/schemas/bridging/getAppMetadataAgentRequest.schema.json index 6a6fd59ec..76f2476b7 100644 --- a/schemas/bridging/getAppMetadataAgentRequest.schema.json +++ b/schemas/bridging/getAppMetadataAgentRequest.schema.json @@ -18,19 +18,22 @@ "description": "A request for metadata about an app", "properties": { "type": { - "title": "GetAppMetadata Request Message Type", - "const": "getAppMetadataRequest" + "$ref": "../api/getAppMetadataRequest.schema.json#/$defs/GetAppMetadataRequestType" }, "payload": { - "title": "GetAppMetadata Request Payload", "type": "object", - "properties": { - "app": { - "$ref": "common.schema.json#/$defs/AppDestination" - } - }, - "required": ["app"], - "additionalProperties": false + "allOf": [{ + "properties": { + "app": { + "$ref": "common.schema.json#/$defs/AppDestination" + } + }, + "required": [ + "app" + ] + },{ + "$ref": "../api/getAppMetadataRequest.schema.json#/$defs/GetAppMetadataRequestPayload" + }] }, "meta": { "title" : "GetAppMetadata Request Metadata", diff --git a/schemas/bridging/getAppMetadataAgentResponse.schema.json b/schemas/bridging/getAppMetadataAgentResponse.schema.json index 65aad070c..549b0e97d 100644 --- a/schemas/bridging/getAppMetadataAgentResponse.schema.json +++ b/schemas/bridging/getAppMetadataAgentResponse.schema.json @@ -18,19 +18,10 @@ "description": "A response to a getAppMetadata request.", "properties": { "type": { - "title": "GetAppMetadata Response Message Type", - "const": "getAppMetadataResponse" + "$ref": "../api/getAppMetadataResponse.schema.json#/$defs/GetAppMetadataResponseType" }, "payload": { - "title": "GetAppMetadata Response Payload", - "type": "object", - "properties": { - "appMetadata": { - "$ref": "../api/api.schema.json#/definitions/AppMetadata" - } - }, - "required": ["appMetadata"], - "additionalProperties": false + "$ref": "../api/getAppMetadataResponse.schema.json#/$defs/GetAppMetadataSuccessResponsePayload" }, "meta": { "title": "GetAppMetadata Response Metadata", diff --git a/schemas/bridging/privateChannelEventListenerAddedAgentRequest.schema.json b/schemas/bridging/privateChannelEventListenerAddedAgentRequest.schema.json index b7fa3a7e8..396a2a316 100644 --- a/schemas/bridging/privateChannelEventListenerAddedAgentRequest.schema.json +++ b/schemas/bridging/privateChannelEventListenerAddedAgentRequest.schema.json @@ -31,7 +31,7 @@ "description": "The id of the PrivateChannel that the event listener was added to." }, "listenerType": { - "$ref": "common.schema.json#/$defs/PrivateChannelEventListenerTypes" + "$ref": "../api/common.schema.json#/$defs/PrivateChannelEventListenerTypes" } }, "additionalProperties": false, diff --git a/schemas/bridging/privateChannelEventListenerRemovedAgentRequest.schema.json b/schemas/bridging/privateChannelEventListenerRemovedAgentRequest.schema.json index 1f0394a25..2e1ce39ca 100644 --- a/schemas/bridging/privateChannelEventListenerRemovedAgentRequest.schema.json +++ b/schemas/bridging/privateChannelEventListenerRemovedAgentRequest.schema.json @@ -31,7 +31,7 @@ "description": "The id of the PrivateChannel that the event listener was removed from." }, "listenerType": { - "$ref": "common.schema.json#/$defs/PrivateChannelEventListenerTypes" + "$ref": "../api/common.schema.json#/$defs/PrivateChannelEventListenerTypes" } }, "additionalProperties": false, From de724bed929226de88a183bab86285040a1987e5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 16 Apr 2024 19:17:49 +0100 Subject: [PATCH 006/152] completing most API call messages in wire protocol --- ...ivateChannel.onDisconnectEvent.schema.json | 0 ...vateChannel.onUnsubscribeEvent.schema.json | 0 .../api/getUserChannelsRequest.schema.json | 2 +- .../api/joinUserChannelRequest.schema.json | 44 +++++++++ .../api/joinUserChannelsResponse.schema.json | 57 +++++++++++ .../leaveCurrentChannelRequest.schema.json | 37 +++++++ .../leaveCurrentChannelResponse.schema.json | 57 +++++++++++ schemas/api/openRequest.schema.json | 44 +++++++++ schemas/api/openResponse.schema.json | 73 ++++++++++++++ ... privateChannelBroadcastEvent.schema.json} | 0 ...rivateChannelDisconnectRequest.schema.json | 44 +++++++++ ...ivateChannelDisconnectResponse.schema.json | 57 +++++++++++ ...hannelEventListenerAddedEvent.schema.json} | 0 ...nnelEventListenerRemovedEvent.schema.json} | 0 ...nnelOnAddContextListenerEvent.schema.json} | 0 ...ivateChannelOnDisconnectEvent.schema.json} | 0 ...vateChannelOnUnsubscribeEvent.schema.json} | 0 .../raiseIntentForContextRequest.schema.json | 47 +++++++++ .../raiseIntentForContextResponse.schema.json | 38 +++++++ schemas/api/raiseIntentRequest.schema.json | 50 ++++++++++ schemas/api/raiseIntentResponse.schema.json | 73 ++++++++++++++ .../api/raiseIntentResultResponse.schema.json | 98 +++++++++++++++++++ .../openAgentErrorResponse.schema.json | 17 +--- schemas/bridging/openAgentRequest.schema.json | 3 +- .../bridging/openAgentResponse.schema.json | 13 +-- .../raiseIntentAgentErrorResponse.schema.json | 17 +--- .../raiseIntentAgentRequest.schema.json | 3 +- .../raiseIntentAgentResponse.schema.json | 13 +-- ...raiseIntentResultAgentResponse.schema.json | 45 +-------- 29 files changed, 733 insertions(+), 99 deletions(-) delete mode 100644 schemas/api/PrivateChannel.onDisconnectEvent.schema.json delete mode 100644 schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json rename schemas/api/{PrivateChannel.broadcastEvent.schema.json => privateChannelBroadcastEvent.schema.json} (100%) create mode 100644 schemas/api/privateChannelDisconnectRequest.schema.json create mode 100644 schemas/api/privateChannelDisconnectResponse.schema.json rename schemas/api/{PrivateChannel.disconnectRequest.schema.json => privateChannelEventListenerAddedEvent.schema.json} (100%) rename schemas/api/{PrivateChannel.disconnectResponse.schema.json => privateChannelEventListenerRemovedEvent.schema.json} (100%) rename schemas/api/{PrivateChannel.eventListenerAddedEvent.schema.json => privateChannelOnAddContextListenerEvent.schema.json} (100%) rename schemas/api/{PrivateChannel.eventListenerRemovedEvent.schema.json => privateChannelOnDisconnectEvent.schema.json} (100%) rename schemas/api/{PrivateChannel.onAddContextListenerEvent.schema.json => privateChannelOnUnsubscribeEvent.schema.json} (100%) diff --git a/schemas/api/PrivateChannel.onDisconnectEvent.schema.json b/schemas/api/PrivateChannel.onDisconnectEvent.schema.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json b/schemas/api/PrivateChannel.onUnsubscribeEvent.schema.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/schemas/api/getUserChannelsRequest.schema.json b/schemas/api/getUserChannelsRequest.schema.json index 37529270d..697c49a1d 100644 --- a/schemas/api/getUserChannelsRequest.schema.json +++ b/schemas/api/getUserChannelsRequest.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/getUserChannelsRequest.schema.json", "type": "object", "title": "GetUserChannels Request", - "description": "Request to return a Channel with an auto-generated identity that is intended for private communication between applications.", + "description": "Request to retrieve a list of the User Channels available for the app to join.", "allOf": [ { "$ref": "appRequest.schema.json" diff --git a/schemas/api/joinUserChannelRequest.schema.json b/schemas/api/joinUserChannelRequest.schema.json index e69de29bb..1edd43ac6 100644 --- a/schemas/api/joinUserChannelRequest.schema.json +++ b/schemas/api/joinUserChannelRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/joinUserChannelRequest.schema.json", + "type": "object", + "title": "JoinUserChannel Request", + "description": "Request to join the app to the specified User channel.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/JoinUserChannelRequestType" + }, + "payload": { + "$ref": "#/$defs/JoinUserChannelRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "JoinUserChannelRequestType": { + "title": "JoinUserChannel Request Message Type", + "const": "joinUserChannelRequest" + }, + "JoinUserChannelRequestPayload": { + "title": "JoinUserChannel Request Payload", + "type": "object", + "properties": { + "channelId": { + "title": "Channel Id", + "description": "The id of the channel to join", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["channelId"] + } + } +} \ No newline at end of file diff --git a/schemas/api/joinUserChannelsResponse.schema.json b/schemas/api/joinUserChannelsResponse.schema.json index e69de29bb..7d7afa875 100644 --- a/schemas/api/joinUserChannelsResponse.schema.json +++ b/schemas/api/joinUserChannelsResponse.schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/joinUserChannelResponse.schema.json", + "type": "object", + "title": "JoinUserChannel Response", + "description": "A response to a joinUserChannel request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/JoinUserChannelResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/JoinUserChannelSuccessResponsePayload" + }, + { + "$ref": "#/$defs/JoinUserChannelErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "JoinUserChannelResponseType": { + "title": "JoinUserChannel Response Message Type", + "const": "joinUserChannelResponse" + }, + "JoinUserChannelSuccessResponsePayload": { + "title": "JoinUserChannel Response Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + }, + "JoinUserChannelErrorResponsePayload": { + "title": "JoinUserChannel Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/leaveCurrentChannelRequest.schema.json b/schemas/api/leaveCurrentChannelRequest.schema.json index e69de29bb..d78488fc6 100644 --- a/schemas/api/leaveCurrentChannelRequest.schema.json +++ b/schemas/api/leaveCurrentChannelRequest.schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelRequest.schema.json", + "type": "object", + "title": "LeaveCurrentChannel Request", + "description": "Request to remove the app from any User channel membership.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/LeaveCurrentChannelRequestType" + }, + "payload": { + "$ref": "#/$defs/LeaveCurrentChannelRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "LeaveCurrentChannelRequestType": { + "title": "LeaveCurrentChannel Request Message Type", + "const": "leaveCurrentChannelRequest" + }, + "LeaveCurrentChannelRequestPayload": { + "title": "LeaveCurrentChannel Request Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/leaveCurrentChannelResponse.schema.json b/schemas/api/leaveCurrentChannelResponse.schema.json index e69de29bb..e0bc33e22 100644 --- a/schemas/api/leaveCurrentChannelResponse.schema.json +++ b/schemas/api/leaveCurrentChannelResponse.schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelResponse.schema.json", + "type": "object", + "title": "LeaveCurrentChannel Response", + "description": "A response to a leaveCurrentChannel request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/LeaveCurrentChannelResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/LeaveCurrentChannelSuccessResponsePayload" + }, + { + "$ref": "#/$defs/LeaveCurrentChannelErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "LeaveCurrentChannelResponseType": { + "title": "LeaveCurrentChannel Response Message Type", + "const": "leaveCurrentChannelResponse" + }, + "LeaveCurrentChannelSuccessResponsePayload": { + "title": "LeaveCurrentChannel Response Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + }, + "LeaveCurrentChannelErrorResponsePayload": { + "title": "LeaveCurrentChannel Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/openRequest.schema.json b/schemas/api/openRequest.schema.json index e69de29bb..77c9ba036 100644 --- a/schemas/api/openRequest.schema.json +++ b/schemas/api/openRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/openRequest.schema.json", + "type": "object", + "title": "Open Request", + "description": "A request to open an application.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/OpenRequestType" + }, + "payload": { + "$ref": "#/$defs/OpenRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "OpenRequestType": { + "title": "Open Request Message Type", + "const": "openRequest" + }, + "OpenRequestPayload": { + "title": "Open Request Payload", + "type": "object", + "properties": { + "app": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": [ + "app" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/openResponse.schema.json b/schemas/api/openResponse.schema.json index e69de29bb..e71d7e6ed 100644 --- a/schemas/api/openResponse.schema.json +++ b/schemas/api/openResponse.schema.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/openResponse.schema.json", + "type": "object", + "title": "Open Response", + "description": "A response to a open request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/OpenResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/OpenSuccessResponsePayload" + }, + { + "$ref": "#/$defs/OpenErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "OpenResponseType": { + "title": "Open Response Message Type", + "const": "openResponse" + }, + "OpenSuccessResponsePayload": { + "title": "Open Response Payload", + "type": "object", + "properties": { + "appIdentifier": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": [ + "appIdentifier" + ], + "additionalProperties": false + }, + "OpenErrorResponsePayload": { + "title": "Open Error Response Payload", + "type": "object", + "properties": { + "error": { + "title": "Open Error Response Payload", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/OpenError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/PrivateChannel.broadcastEvent.schema.json b/schemas/api/privateChannelBroadcastEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.broadcastEvent.schema.json rename to schemas/api/privateChannelBroadcastEvent.schema.json diff --git a/schemas/api/privateChannelDisconnectRequest.schema.json b/schemas/api/privateChannelDisconnectRequest.schema.json new file mode 100644 index 000000000..758005479 --- /dev/null +++ b/schemas/api/privateChannelDisconnectRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectRequest.schema.json", + "type": "object", + "title": "PrivateChannelDisconnect Request", + "description": "Request that indicates that a participant will no longer interact with a specified `PrivateChannel`.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelDisconnectRequestType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelDisconnectRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannel.disconnectRequestType": { + "title": "PrivateChannelDisconnect Request Message Type", + "const": "privateChannelDisconnectRequest" + }, + "PrivateChannel.disconnectRequestPayload": { + "title": "PrivateChannelDisconnect Request Payload", + "type": "object", + "properties": { + "channelId": { + "type": "string", + "title": "Channel Id", + "description": "The Id of the Channel that should be disconnected from" + } + }, + "required": ["channelId"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChannelDisconnectResponse.schema.json b/schemas/api/privateChannelDisconnectResponse.schema.json new file mode 100644 index 000000000..99d54c57d --- /dev/null +++ b/schemas/api/privateChannelDisconnectResponse.schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectResponse.schema.json", + "type": "object", + "title": "PrivateChannelDisconnect Response", + "description": "A response to a privateChannelDisconnect request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelDisconnectResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/PrivateChannelDisconnectSuccessResponsePayload" + }, + { + "$ref": "#/$defs/PrivateChannelDisconnectErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelDisconnectResponseType": { + "title": "PrivateChannelDisconnect Response Message Type", + "const": "privateChannelDisconnectResponse" + }, + "PrivateChannelDisconnectSuccessResponsePayload": { + "title": "PrivateChannelDisconnect Response Payload", + "type": "object", + "properties": {}, + "additionalProperties": false + }, + "PrivateChannelDisconnectErrorResponsePayload": { + "title": "PrivateChannelDisconnect Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/$defs/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/PrivateChannel.disconnectRequest.schema.json b/schemas/api/privateChannelEventListenerAddedEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.disconnectRequest.schema.json rename to schemas/api/privateChannelEventListenerAddedEvent.schema.json diff --git a/schemas/api/PrivateChannel.disconnectResponse.schema.json b/schemas/api/privateChannelEventListenerRemovedEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.disconnectResponse.schema.json rename to schemas/api/privateChannelEventListenerRemovedEvent.schema.json diff --git a/schemas/api/PrivateChannel.eventListenerAddedEvent.schema.json b/schemas/api/privateChannelOnAddContextListenerEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.eventListenerAddedEvent.schema.json rename to schemas/api/privateChannelOnAddContextListenerEvent.schema.json diff --git a/schemas/api/PrivateChannel.eventListenerRemovedEvent.schema.json b/schemas/api/privateChannelOnDisconnectEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.eventListenerRemovedEvent.schema.json rename to schemas/api/privateChannelOnDisconnectEvent.schema.json diff --git a/schemas/api/PrivateChannel.onAddContextListenerEvent.schema.json b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json similarity index 100% rename from schemas/api/PrivateChannel.onAddContextListenerEvent.schema.json rename to schemas/api/privateChannelOnUnsubscribeEvent.schema.json diff --git a/schemas/api/raiseIntentForContextRequest.schema.json b/schemas/api/raiseIntentForContextRequest.schema.json index e69de29bb..758f9dbaf 100644 --- a/schemas/api/raiseIntentForContextRequest.schema.json +++ b/schemas/api/raiseIntentForContextRequest.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentForContextRequest.schema.json", + "type": "object", + "title": "RaiseIntentForContext Request", + "description": "A request to raise an unspecified intent for a specified context.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/RaiseIntentForContextRequestType" + }, + "payload": { + "$ref": "#/$defs/RaiseIntentForContextRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "RaiseIntentForContextRequestType": { + "title": "RaiseIntentForContext Request Message Type", + "const": "raiseIntentForContextRequest" + }, + "RaiseIntentForContextRequestPayload": { + "title": "RaiseIntentForContext Request Payload", + "type": "object", + "properties": { + "context": { + "$ref": "../context/context.schema.json" + }, + "app": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": [ + "context" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json index e69de29bb..1ee14409a 100644 --- a/schemas/api/raiseIntentForContextResponse.schema.json +++ b/schemas/api/raiseIntentForContextResponse.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentForContextResponse.schema.json", + "type": "object", + "title": "RaiseIntentForContext Response", + "description": "A response to a raiseIntentForContext request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/RaiseIntentForContextResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "raiseIntentResponse.schema.json#/$defs/RaiseIntentSuccessResponsePayload" + }, + { + "$ref": "raiseIntentResponse.schema.json#/$defs/RaiseIntentErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "RaiseIntentForContextResponseType": { + "title": "RaiseIntentForContext Response Message Type", + "const": "raiseIntentForContextResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/raiseIntentRequest.schema.json b/schemas/api/raiseIntentRequest.schema.json index e69de29bb..a0ce69d40 100644 --- a/schemas/api/raiseIntentRequest.schema.json +++ b/schemas/api/raiseIntentRequest.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentRequest.schema.json", + "type": "object", + "title": "RaiseIntent Request", + "description": "A request to raise an intent for a context.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/RaiseIntentRequestType" + }, + "payload": { + "$ref": "#/$defs/RaiseIntentRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "RaiseIntentRequestType": { + "title": "RaiseIntent Request Message Type", + "const": "raiseIntentRequest" + }, + "RaiseIntentRequestPayload": { + "title": "RaiseIntent Request Payload", + "type": "object", + "properties": { + "intent": { + "type": "string" + }, + "context": { + "$ref": "../context/context.schema.json" + }, + "app": { + "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + } + }, + "required": [ + "intent", "context" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index e69de29bb..c0f010d57 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentResponse.schema.json", + "type": "object", + "title": "RaiseIntent Response", + "description": "A response to a raiseIntent request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/RaiseIntentResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/RaiseIntentSuccessResponsePayload" + }, + { + "$ref": "#/$defs/RaiseIntentErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "RaiseIntentResponseType": { + "title": "RaiseIntent Response Message Type", + "const": "raiseIntentResponse" + }, + "RaiseIntentSuccessResponsePayload": { + "title": "RaiseIntent Response Payload", + "type": "object", + "properties": { + "intentResolution": { + "$ref": "../api/api.schema.json#/definitions/IntentResolution" + } + }, + "required": [ + "intentResolution" + ], + "additionalProperties": false + }, + "RaiseIntentErrorResponsePayload": { + "title": "RaiseIntent Error Response Payload", + "type": "object", + "properties": { + "error": { + "title": "RaiseIntent Error Response Payload", + "type": "String", + "oneOf": [ + { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + }, + { + "$ref": "../api/api.schema.json#/definitions/BridgingError" + } + ] + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index e69de29bb..7ac244b87 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -0,0 +1,98 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json", + "type": "object", + "title": "RaiseIntentResult Response", + "description": "A secondary response to a request to raise an intent used to deliver the intent result.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/RaiseIntentResultResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/RaiseIntentResultSuccessResponsePayload" + }, + { + "$ref": "#/$defs/RaiseIntentResultErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "RaiseIntentResultResponseType": { + "title": "RaiseIntentResult Response Message Type", + "const": "raiseIntentResultResponse" + }, + "RaiseIntentResultSuccessResponsePayload": { + "title": "RaiseIntent Result Response Payload", + "type": "object", + "properties": { + "intentResult": { + "title": "IntentResult", + "anyOf": [ + { + "type": "object", + "title": "IntentResult Context", + "properties": { + "context": { + "$ref": "../context/context.schema.json" + } + }, + "required": [ + "context" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Channel", + "properties": { + "channel": { + "$ref": "../api/api.schema.json#/definitions/Channel" + } + }, + "required": [ + "channel" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Void", + "properties": {}, + "additionalProperties": false + } + ] + } + }, + "required": [ + "intentResult" + ], + "additionalProperties": false + }, + "RaiseIntentResultErrorResponsePayload": { + "title": "RaiseIntentResult Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/ErrorMessages" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/bridging/openAgentErrorResponse.schema.json b/schemas/bridging/openAgentErrorResponse.schema.json index 6041edcb5..5be7ccf24 100644 --- a/schemas/bridging/openAgentErrorResponse.schema.json +++ b/schemas/bridging/openAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to an open request that contains an error", "properties": { "type": { - "title": "Open Response Message Type", - "const": "openResponse" + "$ref": "../api/openResponse.schema.json#/$defs/OpenResponseType" }, "payload": { - "title": "Open Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "Open Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/OpenError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "required": ["error"], - "additionalProperties": false + "$ref": "../api/openResponse.schema.json#/$defs/OpenErrorResponsePayload" }, "meta": { "title": "Open Response Metadata", diff --git a/schemas/bridging/openAgentRequest.schema.json b/schemas/bridging/openAgentRequest.schema.json index 3b20d30e6..ef308dfce 100644 --- a/schemas/bridging/openAgentRequest.schema.json +++ b/schemas/bridging/openAgentRequest.schema.json @@ -18,8 +18,7 @@ "description": "A request to open an application", "properties": { "type": { - "title": "Open Request Message Type", - "const": "openRequest" + "$ref": "../api/openRequest.schema.json#/$defs/OpenRequestType" }, "payload": { "title": "Open Request Payload", diff --git a/schemas/bridging/openAgentResponse.schema.json b/schemas/bridging/openAgentResponse.schema.json index 5c714f260..c7116178d 100644 --- a/schemas/bridging/openAgentResponse.schema.json +++ b/schemas/bridging/openAgentResponse.schema.json @@ -18,19 +18,10 @@ "description": "A response to an open request", "properties": { "type": { - "title": "Open Response Message Type", - "const": "openResponse" + "$ref": "../api/openResponse.schema.json#/$defs/OpenResponseType" }, "payload": { - "title": "Open Response Payload", - "type": "object", - "properties": { - "appIdentifier": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" - } - }, - "required": ["appIdentifier"], - "additionalProperties": false + "$ref": "../api/openResponse.schema.json#/$defs/OpenSuccessResponsePayload" }, "meta": { "title": "Open Response Metadata", diff --git a/schemas/bridging/raiseIntentAgentErrorResponse.schema.json b/schemas/bridging/raiseIntentAgentErrorResponse.schema.json index c7dd9dcfb..9dea12a59 100644 --- a/schemas/bridging/raiseIntentAgentErrorResponse.schema.json +++ b/schemas/bridging/raiseIntentAgentErrorResponse.schema.json @@ -18,23 +18,10 @@ "description": "A response to a request to raise an intent that contains an error.", "properties": { "type": { - "title": "RaiseIntent Response Message type", - "const": "raiseIntentResponse" + "$ref": "../api/raiseIntentResponse.schema.json#/$defs/RaiseIntentResponseType" }, "payload": { - "title": "RaiseIntent Error Response Payload", - "type": "object", - "properties": { - "error": { - "title": "RaiseIntent Error Message", - "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/ResolveError" }, - { "$ref": "../api/api.schema.json#/definitions/BridgingError" } - ] - } - }, - "required": ["error"], - "additionalProperties": false + "$ref": "../api/raiseIntentResponse.schema.json#/$defs/RaiseIntentErrorResponsePayload" }, "meta": { "title": "RaiseIntent Response Metadata", diff --git a/schemas/bridging/raiseIntentAgentRequest.schema.json b/schemas/bridging/raiseIntentAgentRequest.schema.json index 4a9921859..a6ceca451 100644 --- a/schemas/bridging/raiseIntentAgentRequest.schema.json +++ b/schemas/bridging/raiseIntentAgentRequest.schema.json @@ -18,8 +18,7 @@ "description": "A request to raise an intent.", "properties": { "type": { - "title": "RaiseIntent Request Message type", - "const": "raiseIntentRequest" + "$ref": "../api/raiseIntentRequest.schema.json#/$defs/RaiseIntentRequestType" }, "payload": { "title": "RaiseIntent Request Payload", diff --git a/schemas/bridging/raiseIntentAgentResponse.schema.json b/schemas/bridging/raiseIntentAgentResponse.schema.json index 30c0ec397..8ad8c8b23 100644 --- a/schemas/bridging/raiseIntentAgentResponse.schema.json +++ b/schemas/bridging/raiseIntentAgentResponse.schema.json @@ -18,19 +18,10 @@ "description": "A response to a request to raise an intent.", "properties": { "type": { - "title": "RaiseIntent Response Message type", - "const": "raiseIntentResponse" + "$ref": "../api/raiseIntentResponse.schema.json#/$defs/RaiseIntentResponseType" }, "payload": { - "title": "RaiseIntent Response Payload", - "type": "object", - "properties": { - "intentResolution": { - "$ref": "../api/api.schema.json#/definitions/IntentResolution" - } - }, - "required": ["intentResolution"], - "additionalProperties": false + "$ref": "../api/raiseIntentResponse.schema.json#/$defs/RaiseIntentSuccessResponsePayload" }, "meta": { "title": "RaiseIntent Response Metadata", diff --git a/schemas/bridging/raiseIntentResultAgentResponse.schema.json b/schemas/bridging/raiseIntentResultAgentResponse.schema.json index e5b8b36c7..f97331b2e 100644 --- a/schemas/bridging/raiseIntentResultAgentResponse.schema.json +++ b/schemas/bridging/raiseIntentResultAgentResponse.schema.json @@ -18,49 +18,10 @@ "description": "A secondary response to a request to raise an intent used to deliver the intent result", "properties": { "type": { - "title": "RaiseIntent Result Response Message type", - "const": "raiseIntentResultResponse" + "$ref": "../api/raiseIntentResultResponse.schema.json#/$defs/RaiseIntentResultResponseType" }, "payload": { - "title": "RaiseIntent Result Response Payload", - "type": "object", - "properties": { - "intentResult": { - "title": "IntentResult", - "anyOf": [ - { - "type": "object", - "title": "IntentResult Context", - "properties": { - "context": { - "$ref": "../context/context.schema.json" - } - }, - "required": ["context"], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Channel", - "properties": { - "channel": { - "$ref": "../api/api.schema.json#/definitions/Channel" - } - }, - "required": ["channel"], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Void", - "properties": {}, - "additionalProperties": false - } - ] - } - }, - "required": ["intentResult"], - "additionalProperties": false + "$ref": "../api/raiseIntentResultResponse.schema.json#/$defs/RaiseIntentResultSuccessResponsePayload" }, "meta": { "title": "RaiseIntent Result Response Metadata", @@ -70,4 +31,4 @@ "additionalProperties": false } } -} +} \ No newline at end of file From 8bcaab21054782bb9451f78bcc957cd521c4a134 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 17 Apr 2024 13:10:46 +0100 Subject: [PATCH 007/152] adding messages for managing context and intent listeners --- .../api/addContextListenerRequest.schema.json | 65 +++++++++++++++++++ .../addContextListenerResponse.schema.json | 63 ++++++++++++++++++ .../api/addIntentListenerRequest.scheam.json | 46 +++++++++++++ .../api/addIntentListenerResponse.schema.json | 63 ++++++++++++++++++ schemas/api/common.schema.json | 5 ++ ...textListenerUnsubscribeRequest.schema.json | 44 +++++++++++++ ...extListenerUnsubscribeResponse.schema.json | 29 +++++++++ ...tentListenerUnsubscribeRequest.schema.json | 44 +++++++++++++ ...entListenerUnsubscribeResponse.schema.json | 29 +++++++++ 9 files changed, 388 insertions(+) diff --git a/schemas/api/addContextListenerRequest.schema.json b/schemas/api/addContextListenerRequest.schema.json index e69de29bb..a001edb53 100644 --- a/schemas/api/addContextListenerRequest.schema.json +++ b/schemas/api/addContextListenerRequest.schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addContextListenerRequest.schema.json", + "type": "object", + "title": "AddContextListener Request", + "description": "A request to add a context listener to a specified Channel OR to the current user channel.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddContextListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/AddContextListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddContextListenerRequestType": { + "title": "AddContextListener Request Message Type", + "const": "addContextListenerRequest" + }, + "AddContextListenerRequestPayload": { + "title": "AddContextListener Request Payload", + "type": "object", + "properties": { + "channelId": { + "title": "Channel Id", + "description": "The id of the channel to add the listener to or `null` indicating that it should listen to the current user channel (at the time of broadcast).", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "contextType": { + "title": "Context type", + "description": "The type of context to listen for OR `null` indicating that it should listen to all context types.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "channelId", "contextType" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/addContextListenerResponse.schema.json b/schemas/api/addContextListenerResponse.schema.json index e69de29bb..04a4efd66 100644 --- a/schemas/api/addContextListenerResponse.schema.json +++ b/schemas/api/addContextListenerResponse.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addContextListenerResponse.schema.json", + "type": "object", + "title": "AddContextListener Response", + "description": "A response to a addContextListener request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddContextListenerResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/AddContextListenerSuccessResponsePayload" + }, + { + "$ref": "#/$defs/AddContextListenerErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddContextListenerResponseType": { + "title": "AddContextListener Response Message Type", + "const": "addContextListenerResponse" + }, + "AddContextListenerSuccessResponsePayload": { + "title": "AddContextListener Response Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ] + }, + "AddContextListenerErrorResponsePayload": { + "title": "", + "type": "object", + "properties": { + "error": { + "$ref": "../api/api.schema.json#/definitions/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/addIntentListenerRequest.scheam.json b/schemas/api/addIntentListenerRequest.scheam.json index e69de29bb..b639d39aa 100644 --- a/schemas/api/addIntentListenerRequest.scheam.json +++ b/schemas/api/addIntentListenerRequest.scheam.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json", + "type": "object", + "title": "AddIntentListener Request", + "description": "A request to add an Intent listener for a specified intent type.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddIntentListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/AddIntentListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddIntentListenerRequestType": { + "title": "AddIntentListener Request Message Type", + "const": "addIntentListenerRequest" + }, + "AddIntentListenerRequestPayload": { + "title": "AddIntentListener Request Payload", + "type": "object", + "properties": { + "intent": { + "title": "Intent name", + "description": "The name of the intent to listen for.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "intent" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/addIntentListenerResponse.schema.json b/schemas/api/addIntentListenerResponse.schema.json index e69de29bb..7a2f7ddd8 100644 --- a/schemas/api/addIntentListenerResponse.schema.json +++ b/schemas/api/addIntentListenerResponse.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addIntentListenerResponse.schema.json", + "type": "object", + "title": "AddIntentListener Response", + "description": "A response to a addIntentListener request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddIntentListenerResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/AddIntentListenerSuccessResponsePayload" + }, + { + "$ref": "#/$defs/AddIntentListenerErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddIntentListenerResponseType": { + "title": "AddIntentListener Response Message Type", + "const": "addIntentListenerResponse" + }, + "AddIntentListenerSuccessResponsePayload": { + "title": "AddIntentListener Response Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ] + }, + "AddIntentListenerErrorResponsePayload": { + "title": "", + "type": "object", + "properties": { + "error": { + "$ref": "../api/api.schema.json#/definitions/ResolveError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json index 7aec93911..42879c684 100644 --- a/schemas/api/common.schema.json +++ b/schemas/api/common.schema.json @@ -20,6 +20,11 @@ "type": "string", "description": "Unique identifier for an event message sent from a Desktop Agent to an app." }, + "ListenerUuid": { + "title": "Listener UUID", + "type": "string", + "description": "Unique identifier for a `listener` object returned by a Desktop Agent to an app in response to addCOntextListener or addIntentListener and used to identify it in messages (e.g. when unsubscribing)." + }, "Timestamp": { "title": "Timestamp", "type": "string", diff --git a/schemas/api/contextListenerUnsubscribeRequest.schema.json b/schemas/api/contextListenerUnsubscribeRequest.schema.json index e69de29bb..d0f1c3abc 100644 --- a/schemas/api/contextListenerUnsubscribeRequest.schema.json +++ b/schemas/api/contextListenerUnsubscribeRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeRequest.schema.json", + "type": "object", + "title": "ContextListenerUnsubscribe Request", + "description": "A request to unsubscribe a context listener.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/ContextListenerUnsubscribeRequestType" + }, + "payload": { + "$ref": "#/$defs/ContextListenerUnsubscribeRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "ContextListenerUnsubscribeRequestType": { + "title": "ContextListenerUnsubscribe Request Message Type", + "const": "contextListenerUnsubscribeRequest" + }, + "ContextListenerUnsubscribeRequestPayload": { + "title": "ContextListenerUnsubscribe Request Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/contextListenerUnsubscribeResponse.schema.json b/schemas/api/contextListenerUnsubscribeResponse.schema.json index e69de29bb..30beb4150 100644 --- a/schemas/api/contextListenerUnsubscribeResponse.schema.json +++ b/schemas/api/contextListenerUnsubscribeResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeResponse.schema.json", + "type": "object", + "title": "ContextListenerUnsubscribe Response", + "description": "A response to a request to a contextListenerUnsubscribe request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/ContextListenerUnsubscribeResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "ContextListenerUnsubscribeResponseType": { + "title": "ContextListenerUnsubscribe Response Message Type", + "const": "contextListenerUnsubscribeResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/intentListenerUnsubscribeRequest.schema.json b/schemas/api/intentListenerUnsubscribeRequest.schema.json index e69de29bb..1de21f601 100644 --- a/schemas/api/intentListenerUnsubscribeRequest.schema.json +++ b/schemas/api/intentListenerUnsubscribeRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeRequest.schema.json", + "type": "object", + "title": "IntentListenerUnsubscribe Request", + "description": "A request to unsubscribe a context listener.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/IntentListenerUnsubscribeRequestType" + }, + "payload": { + "$ref": "#/$defs/IntentListenerUnsubscribeRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "IntentListenerUnsubscribeRequestType": { + "title": "IntentListenerUnsubscribe Request Message Type", + "const": "intentListenerUnsubscribeRequest" + }, + "IntentListenerUnsubscribeRequestPayload": { + "title": "IntentListenerUnsubscribe Request Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/intentListenerUnsubscribeResponse.schema.json b/schemas/api/intentListenerUnsubscribeResponse.schema.json index e69de29bb..8396c87df 100644 --- a/schemas/api/intentListenerUnsubscribeResponse.schema.json +++ b/schemas/api/intentListenerUnsubscribeResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json", + "type": "object", + "title": "IntentListenerUnsubscribe Response", + "description": "A response to a request to a intentListenerUnsubscribe request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/IntentListenerUnsubscribeResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "IntentListenerUnsubscribeResponseType": { + "title": "IntentListenerUnsubscribe Response Message Type", + "const": "intentListenerUnsubscribeResponse" + } + } +} \ No newline at end of file From bc8ec0f18752c6c62fbaf1ee7e29869750447909 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 17 Apr 2024 14:26:48 +0100 Subject: [PATCH 008/152] Completing private channel message exchanges and all event messages --- schemas/api/agentEventMessage.schema.json | 9 +-- schemas/api/agentResponseMessage.schema.json | 4 +- schemas/api/appRequest.schema.json | 6 +- schemas/api/channelChangedEvent..schema.json | 53 ++++++++++++++++ schemas/api/common.schema.json | 2 +- .../privateChannelBroadcastEvent.schema.json | 0 ...ChannelEventListenerAddedEvent.schema.json | 0 ...annelEventListenerRemovedEvent.schema.json | 0 ...annelOnAddContextListenerEvent.schema.json | 59 +++++++++++++++++ ...rivateChannelOnDisconnectEvent.schema.json | 46 ++++++++++++++ ...ivateChannelOnUnsubscribeEvent.schema.json | 59 +++++++++++++++++ ...nsubscribeEventListenerRequest.schema.json | 44 +++++++++++++ ...subscribeEventListenerResponse.schema.json | 29 +++++++++ ...ChanneladdEventListenerRequest.schema.json | 59 +++++++++++++++++ ...hanneladdEventListenerResponse.schema.json | 63 +++++++++++++++++++ 15 files changed, 423 insertions(+), 10 deletions(-) delete mode 100644 schemas/api/privateChannelBroadcastEvent.schema.json delete mode 100644 schemas/api/privateChannelEventListenerAddedEvent.schema.json delete mode 100644 schemas/api/privateChannelEventListenerRemovedEvent.schema.json create mode 100644 schemas/api/privateChannelUnsubscribeEventListenerRequest.schema.json create mode 100644 schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json create mode 100644 schemas/api/privateChanneladdEventListenerRequest.schema.json create mode 100644 schemas/api/privateChanneladdEventListenerResponse.schema.json diff --git a/schemas/api/agentEventMessage.schema.json b/schemas/api/agentEventMessage.schema.json index f75da99bd..4d083918c 100644 --- a/schemas/api/agentEventMessage.schema.json +++ b/schemas/api/agentEventMessage.schema.json @@ -9,12 +9,9 @@ "title": "Message Type", "type": "string", "enum": [ - "PrivateChannel.broadcastEvent", - "PrivateChannel.eventListenerAddedEvent", - "PrivateChannel.eventListenerRemovedEvent", - "PrivateChannel.onAddContextListenerEvent", - "PrivateChannel.onDisconnectEvent", - "PrivateChannel.onUnsubscribeEvent", + "privateChannelOnAddContextListenerEvent", + "privateChannelOnUnsubscribeEvent", + "privateChannelOnDisconnectEvent", "channelChangedEvent" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." diff --git a/schemas/api/agentResponseMessage.schema.json b/schemas/api/agentResponseMessage.schema.json index 65e2057af..67125df4e 100644 --- a/schemas/api/agentResponseMessage.schema.json +++ b/schemas/api/agentResponseMessage.schema.json @@ -29,7 +29,9 @@ "raiseIntentResultResponse", "contextListenerUnsubscribeResponse", "intentListenerUnsubscribeResponse", - "PrivateChannel.disconnectResponse" + "privateChannelAddEventListenerResponse", + "privateChannelUnsubscribeEventListenerResponse", + "privateChannelDisconnectResponse" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." }, diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index 068fe5579..56a9ba6ff 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -24,11 +24,13 @@ "joinUserChannelRequest", "leaveCurrentChannelRequest", "openRequest", - "PrivateChannel.disconnectRequest", "raiseIntentRequest", "raiseIntentForContextRequest", "contextListenerUnsubscribeRequest", - "intentListenerUnsubscribeRequest" + "intentListenerUnsubscribeRequest", + "privateChannelAddEventListenerRequest", + "privateChannelUnsubscribeEventListenerRequest", + "privateChannelDisconnectRequest" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Request' appended." }, diff --git a/schemas/api/channelChangedEvent..schema.json b/schemas/api/channelChangedEvent..schema.json index e69de29bb..7bc4d5cf3 100644 --- a/schemas/api/channelChangedEvent..schema.json +++ b/schemas/api/channelChangedEvent..schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/channelChangedEvent.schema.json", + "type": "object", + "title": "channelChanged Event", + "description": "An event message from the Desktop Agent to an app indicating that its current user channel has changed.", + "allOf": [ + { + "$ref": "agentEventMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/ChannelChangedEventType" + }, + "payload": { + "$ref": "#/$defs/ChannelChangedEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "ChannelChangedEventType": { + "title": "ChannelChanged Event Message Type", + "const": "channelChangedEvent" + }, + "ChannelChangedEventPayload": { + "title": "channelChanged Event Payload", + "type": "object", + "properties": { + "newChannelId": { + "title": "New Channel Id", + "description": "The Id of the channel that the app was added to or `null` if it was removed from a channel.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "newChannelId" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json index 42879c684..901b36b6b 100644 --- a/schemas/api/common.schema.json +++ b/schemas/api/common.schema.json @@ -23,7 +23,7 @@ "ListenerUuid": { "title": "Listener UUID", "type": "string", - "description": "Unique identifier for a `listener` object returned by a Desktop Agent to an app in response to addCOntextListener or addIntentListener and used to identify it in messages (e.g. when unsubscribing)." + "description": "Unique identifier for a `listener` object returned by a Desktop Agent to an app in response to addContextListener, addIntentListener or one of the PrivateChannel event listeners and used to identify it in messages (e.g. when unsubscribing)." }, "Timestamp": { "title": "Timestamp", diff --git a/schemas/api/privateChannelBroadcastEvent.schema.json b/schemas/api/privateChannelBroadcastEvent.schema.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/schemas/api/privateChannelEventListenerAddedEvent.schema.json b/schemas/api/privateChannelEventListenerAddedEvent.schema.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/schemas/api/privateChannelEventListenerRemovedEvent.schema.json b/schemas/api/privateChannelEventListenerRemovedEvent.schema.json deleted file mode 100644 index e69de29bb..000000000 diff --git a/schemas/api/privateChannelOnAddContextListenerEvent.schema.json b/schemas/api/privateChannelOnAddContextListenerEvent.schema.json index e69de29bb..f158e48a8 100644 --- a/schemas/api/privateChannelOnAddContextListenerEvent.schema.json +++ b/schemas/api/privateChannelOnAddContextListenerEvent.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelOnAddContextListenerEvent.schema.json", + "type": "object", + "title": "privateChannelOnAddContextListener Event", + "description": "An event message from the Desktop Agent to an app indicating that another app has added a context listener to a specific PrivateChannel.", + "allOf": [ + { + "$ref": "agentEventMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelOnAddContextListenerEventType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelOnAddContextListenerEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelOnAddContextListenerEventType": { + "title": "PrivateChannelOnAddContextListener Event Message Type", + "const": "privateChannelOnAddContextListenerEvent" + }, + "PrivateChannelOnAddContextListenerEventPayload": { + "title": "privateChannelOnAddContextListener Event Payload", + "type": "object", + "properties": { + "privateChannelId": { + "type": "string", + "title": "Private Channel Id", + "description": "The Id of the PrivateChannel that the listener was added to." + }, + "contextType": { + "title": "Context type", + "description": "The type of the context listener added to the channel by another app, or null if it will listen to all types.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "privateChannelId", + "contextType" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChannelOnDisconnectEvent.schema.json b/schemas/api/privateChannelOnDisconnectEvent.schema.json index e69de29bb..b5f6ad649 100644 --- a/schemas/api/privateChannelOnDisconnectEvent.schema.json +++ b/schemas/api/privateChannelOnDisconnectEvent.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelOnDisconnectEvent.schema.json", + "type": "object", + "title": "privateChannelOnDisconnect Event", + "description": "An event message from the Desktop Agent to an app indicating that another app has disconnected from a specific PrivateChannel and will no longer interact with it.", + "allOf": [ + { + "$ref": "agentEventMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelOnDisconnectEventType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelOnDisconnectEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelOnDisconnectEventType": { + "title": "PrivateChannelOnDisconnect Event Message Type", + "const": "privateChannelOnDisconnectEvent" + }, + "PrivateChannelOnDisconnectEventPayload": { + "title": "privateChannelOnDisconnect Event Payload", + "type": "object", + "properties": { + "privateChannelId": { + "type": "string", + "title": "Private Channel Id", + "description": "The Id of the PrivateChannel that the app has disconnected from." + } + }, + "additionalProperties": false, + "required": [ + "privateChannelId" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json index e69de29bb..b4ee0e01a 100644 --- a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json +++ b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelOnUnsubscribeEvent.schema.json", + "type": "object", + "title": "PrivateChannelOnUnsubscribeEvent Event", + "description": "An event message from the Desktop Agent to an app indicating that another app has unsubscribed a context listener from a specific PrivateChannel.", + "allOf": [ + { + "$ref": "agentEventMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelOnUnsubscribeEventType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelOnUnsubscribeEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelOnUnsubscribeEventType": { + "title": "PrivateChannelOnUnsubscribe Event Message Type", + "const": "privateChannelOnUnsubscribeEvent" + }, + "PrivateChannelOnUnsubscribeEventPayload": { + "title": "privateChannelOnUnsubscribe Event Payload", + "type": "object", + "properties": { + "privateChannelId": { + "type": "string", + "title": "Private Channel Id", + "description": "The Id of the PrivateChannel that the listener was unsubscribed from." + }, + "contextType": { + "title": "Context type", + "description": "The type of the context listener unsubscribed from the channel by another app, or null if it was listening to all types.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "privateChannelId", + "contextType" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChannelUnsubscribeEventListenerRequest.schema.json b/schemas/api/privateChannelUnsubscribeEventListenerRequest.schema.json new file mode 100644 index 000000000..658ebcce8 --- /dev/null +++ b/schemas/api/privateChannelUnsubscribeEventListenerRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerRequest.schema.json", + "type": "object", + "title": "PrivateChannelUnsubscribeEventListener Request", + "description": "A request to unsubscribe a context listener.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelUnsubscribeEventListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelUnsubscribeEventListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelUnsubscribeEventListenerRequestType": { + "title": "PrivateChannelUnsubscribeEventListener Request Message Type", + "const": "privateChannelUnsubscribeEventListenerRequest" + }, + "PrivateChannelUnsubscribeEventListenerRequestPayload": { + "title": "PrivateChannelUnsubscribeEventListener Request Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json b/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json new file mode 100644 index 000000000..019331b29 --- /dev/null +++ b/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerResponse.schema.json", + "type": "object", + "title": "PrivateChannelUnsubscribeEventListener Response", + "description": "A response to a privateChannelUnsubscribeEventListener request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelUnsubscribeEventListenerResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelUnsubscribeEventListenerResponseType": { + "title": "PrivateChannelUnsubscribeEventListener Response Message Type", + "const": "privateChannelUnsubscribeEventListenerResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChanneladdEventListenerRequest.schema.json b/schemas/api/privateChanneladdEventListenerRequest.schema.json new file mode 100644 index 000000000..f45cf69ea --- /dev/null +++ b/schemas/api/privateChanneladdEventListenerRequest.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelAddEventListenerRequest.schema.json", + "type": "object", + "title": "PrivateChannelAddEventListener Request", + "description": "An event message from the Desktop Agent to an app indicating that another app has added a context listener to a specific PrivateChannel.", + "allOf": [ + { + "$ref": "agentRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelAddEventListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/PrivateChannelAddEventListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelAddEventListenerRequestType": { + "title": "PrivateChannelAddEventListener Request Message Type", + "const": "privateChannelAddEventListenerRequest" + }, + "PrivateChannelAddEventListenerRequestPayload": { + "title": "privateChannelAddEventListener Event Payload", + "type": "object", + "properties": { + "privateChannelId": { + "type": "string", + "title": "Private Channel Id", + "description": "The Id of the PrivateChannel that the listener should be added to." + }, + "contextType": { + "title": "Context type", + "description": "The type of the context listener to add to the channel, or null if it should listen to all types.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "privateChannelId", + "contextType" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/privateChanneladdEventListenerResponse.schema.json b/schemas/api/privateChanneladdEventListenerResponse.schema.json new file mode 100644 index 000000000..f33847bd5 --- /dev/null +++ b/schemas/api/privateChanneladdEventListenerResponse.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelAddEventListenerResponse.schema.json", + "type": "object", + "title": "PrivateChannelAddEventListener Response", + "description": "A response to a privateChannelAddEventListener request.", + "allOf": [ + { + "$ref": "agentResponseMessage.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/PrivateChannelAddEventListenerResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/PrivateChannelAddEventListenerSuccessResponsePayload" + }, + { + "$ref": "#/$defs/PrivateChannelAddEventListenerErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "PrivateChannelAddEventListenerResponseType": { + "title": "PrivateChannelAddEventListener Response Message Type", + "const": "privateChannelAddEventListenerResponse" + }, + "PrivateChannelAddEventListenerSuccessResponsePayload": { + "title": "PrivateChannelAddEventListener Response Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ] + }, + "PrivateChannelAddEventListenerErrorResponsePayload": { + "title": "", + "type": "object", + "properties": { + "error": { + "$ref": "../api/api.schema.json#/definitions/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file From fbf7598d0438893e383045bd835b12ddca3ab4e5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 17 Apr 2024 14:30:38 +0100 Subject: [PATCH 009/152] prettier --- src/api/DesktopAgent.ts | 16 +++- src/api/GetAgent.ts | 162 ++++++++++++++++++++++------------------ 2 files changed, 100 insertions(+), 78 deletions(-) diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts index ca86d9d96..ea2b36167 100644 --- a/src/api/DesktopAgent.ts +++ b/src/api/DesktopAgent.ts @@ -528,14 +528,22 @@ export interface DesktopAgent { /** * Use by getAgent() to validate app identity. Apps should not call this function directly. - * + * * See getAgent.md for instructions. - * + * * Either appId, appDUrl, or both may be provided. This logic is covered int supported_platforms.md. - * + * * This function is optional but recommended. */ - validateAppIdentity?({appId, appDUrl, instanceUuid} : { appId?: string, appDUrl?: string, instanceUuid?:string}) : Promise + validateAppIdentity?({ + appId, + appDUrl, + instanceUuid, + }: { + appId?: string; + appDUrl?: string; + instanceUuid?: string; + }): Promise; //--------------------------------------------------------------------------------------------- // Deprecated function signatures diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index cf58562bc..50b640827 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -2,129 +2,143 @@ * Typescript related to the getAgent() function. Note that GetAgent.md is a copy of this file which has been modified * for display in markdown format. */ -import { DesktopAgent } from "./DesktopAgent"; -import { ImplementationMetadata } from "./ImplementationMetadata"; -import { AppMetadata } from "./AppMetadata"; +import { DesktopAgent } from './DesktopAgent'; +import { ImplementationMetadata } from './ImplementationMetadata'; +import { AppMetadata } from './AppMetadata'; -/** +/** * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports * injection or by using the FDC3 Web Connection Protocol (WCP) to establish a connection * with a browser-resident Desktop Agent. - * + * * @param {GetAgentParams} params Required parameters for the function. - * + * * @return A promise that resolves to an object which contains a DesktopAgent, or which * rejects with a string from the AgentError union if a DesktopAgent cannot be established. - * - * @example - * const { desktopAgent: fdc3 } = await getAgent({ + * + * @example + * const { desktopAgent: fdc3 } = await getAgent({ * appId: “myApp@myorg.com/appd” - * }); - * + * }); + * * * @example Using appDUrl - * const { desktopAgent: fdc3 } = await getAgent({ + * const { desktopAgent: fdc3 } = await getAgent({ * appId: “myApp”, * appDUrl: "myorg.com/appd" - * }); + * }); */ -export type GetAgentFunction = ( - params: GetAgentParams, +export type GetAgentFunction = ( + params: GetAgentParams ) => Promise<{ - desktopAgent: DesktopAgent, - implementationMetadata: ImplementationMetadata, - appMetadata: AppMetadata -}>; + desktopAgent: DesktopAgent; + implementationMetadata: ImplementationMetadata; + appMetadata: AppMetadata; +}>; -/** - * @typedef {Object} GetAgentParams Type representing parameters passed to the +/** + * @typedef {Object} GetAgentParams Type representing parameters passed to the * getAgent function. - * + * * @property {string} appId The fully qualified appId that represents the application. * (in the form @) - * + * * @property {URL} appDUrl A URL that points to an appD record for the application. * Used as an alternative to providing a fully qualified appId. - * - * @property {number} timeout Number of milliseconds to allow for establishing a + * + * @property {number} timeout Number of milliseconds to allow for establishing a * DesktopAgent. When the timeout expires, the optional provided failover function will be * run. Default 750. - * - * @property {boolean} channelSelector Flag indicating that the application + * + * @property {boolean} channelSelector Flag indicating that the application * requires getAgent() to create a channel selector UI. Defaults to true. - * - * @property {boolean} intentResolver Flag indicating that the application + * + * @property {boolean} intentResolver Flag indicating that the application * requires getAgent() to create an intent resolver UI. Defaults to true. - * + * * @property {function} failover A optional function that can establish connectivity * to a DesktopAgent if standard mechanisms fail. If a WindowProxy or URL is provided * then getAgent() will re-run its internal algorithm with those objects (restarting * the timeout). The function may also simply return a DesktopAgent. - */ + */ export type GetAgentParams = { - timeout ?: number, // Defaults to 750 - appId ?: string, - appDUrl ?: string, - failover ?: (args: GetAgentParams) => Promise -}; + timeout?: number; // Defaults to 750 + appId?: string; + appDUrl?: string; + failover?: (args: GetAgentParams) => Promise; +}; -/** +/** * Represents the set of errors that may be returned by getAgent() if connectivity * cannot be established with a DesktopAgent. - * + * * "AgentNotFound" - Returned when connectivity to a DA cannot be established. - * + * * "InvalidAgent" - Returned when a DA does not conform to the Web Connection Protocol (WCP). - * + * * "IdentityValidationFailed" - Returned when the app fails DA identity verification. - * + * * "NoAppIdentityProvided" - Returned when the DA refuses a connection from application. - * + * * "AccessDenied" - TODO? Returned when the app does not pass one of the required identity parameters indicating an app directory record. - * + * * "InvalidFailover" - Returned when the failover function itself, or its resolution is not the right type. - * + * * "ReestablishConnectionFailed" - Returned when reestablishment of an instance via persisted StorageSession data fails (e.g. after a page navigation) - * + * * "ErrorOnConnect" - Returned when any other error or exception occurs. */ -export type AgentError = "AgentNotFound" | "InvalidAgent" | "IdentityValidationFailed" | "NoAppIdentityProvided" | "AccessDenied" | "ErrorOnConnect" | "InvalidFailover" | "ReestablishConnectionFailed"; +export type AgentError = + | 'AgentNotFound' + | 'InvalidAgent' + | 'IdentityValidationFailed' + | 'NoAppIdentityProvided' + | 'AccessDenied' + | 'ErrorOnConnect' + | 'InvalidFailover' + | 'ReestablishConnectionFailed'; /** * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. * getAgent() will use this connection information when it exists to ensure a consistent instanceId. */ -export type DesktopAgentDetails = { - /** The type of DA. Prevents an inadvertent switch to a different agent.*/ - agentType: WebDesktopAgentType, - - /** May contain the URL that was used to connect to the prior DA. - * This may have been provided by a parent window that has since - * closed. It may therefore be used to open a new window/iframe and restart the DA. */ - url?: string, - - /** The prior appId set by this window. If a appDUrl was previously set then - * this appId will be set to the the representative fully qualified appId. */ - appId: string, - - /** The instanceId that was issued by the DA to this window. */ - instanceId: string, - - /** The instanceUuid that was previously issued by the DA. - * This MUST be passed when connecting to the DA. The DA will use - * this to determine whether the app has previously connected and - * which instance it was. The DA uses this to reissue the same instanceId. - * - * The instanceUuid is secret. It should never be shared with other applications - * and is not available through the FDC3 API. */ - instanceUuid: string -} - +export type DesktopAgentDetails = { + /** The type of DA. Prevents an inadvertent switch to a different agent.*/ + + agentType: WebDesktopAgentType; + + /** May contain the URL that was used to connect to the prior DA. + * This may have been provided by a parent window that has since + * closed. It may therefore be used to open a new window/iframe and restart the DA. */ + + url?: string; + + /** The prior appId set by this window. If a appDUrl was previously set then + * this appId will be set to the the representative fully qualified appId. */ + + appId: string; + + /** The instanceId that was issued by the DA to this window. */ + + instanceId: string; + + /** The instanceUuid that was previously issued by the DA. + * This MUST be passed when connecting to the DA. The DA will use + * this to determine whether the app has previously connected and + * which instance it was. The DA uses this to reissue the same instanceId. + * + * The instanceUuid is secret. It should never be shared with other applications + * and is not available through the FDC3 API. */ + + instanceUuid: string; +}; + /** Specifies the means by which a connection to the DA is made. * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. * "PARENT" - The DA runs in a parent window or iframe. * "URL" - The DA is loaded as an iframe in the app window. - */ -export type WebDesktopAgentType = "INJECTED" | "PARENT" | "URL"; \ No newline at end of file + */ + +export type WebDesktopAgentType = 'INJECTED' | 'PARENT' | 'URL'; From 971f62d3a2e0af588e86ab8629b0d9c5e21cc0a2 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 19 Apr 2024 12:39:35 +0100 Subject: [PATCH 010/152] minor jsdoc correction --- src/api/GetAgent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 50b640827..1ba2532af 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -21,7 +21,7 @@ import { AppMetadata } from './AppMetadata'; * appId: “myApp@myorg.com/appd” * }); * - * * @example Using appDUrl + * @example Using appDUrl * const { desktopAgent: fdc3 } = await getAgent({ * appId: “myApp”, * appDUrl: "myorg.com/appd" From 68121b7028ec30e6d0820d88c5aa1b0f44d8713e Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Apr 2024 13:25:50 +0100 Subject: [PATCH 011/152] Updating getAgent and Errors docs pages for fdc3-for-the-web --- docs/api/ref/Errors.md | 117 ++++++++++++++++++++++++++++++++++++--- docs/api/ref/GetAgent.md | 99 ++++++++++++++++++--------------- website/sidebars.json | 1 + 3 files changed, 165 insertions(+), 52 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 90af81cd5..14e96edd2 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -9,6 +9,8 @@ FDC3 API operations may sometimes result in an error, which must be returned to ## `ChannelError` +Contains constants representing the errors that can be encountered when calling channels using the [`joinUserChannel`](DesktopAgent#joinuserchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](Channel#getcurrentcontext), [`broadcast`](Channel#broadcast) or [`addContextListener`](Channel#addcontextlistener) methods on the `Channel` object. + @@ -75,8 +77,6 @@ public static class ChannelError -Contains constants representing the errors that can be encountered when calling channels using the [`joinUserChannel`](DesktopAgent#joinuserchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](Channel#getcurrentcontext), [`broadcast`](Channel#broadcast) or [`addContextListener`](Channel#addcontextlistener) methods on the `Channel` object. - **See also:** - [`DesktopAgent.createPrivateChannel`](DesktopAgent#createprivatechannel) @@ -88,6 +88,8 @@ Contains constants representing the errors that can be encountered when calling ## `OpenError` +Contains constants representing the errors that can be encountered when calling the [`open`](DesktopAgent#open) method on the [DesktopAgent](DesktopAgent) object. + @@ -161,14 +163,14 @@ public static class OpenError -Contains constants representing the errors that can be encountered when calling the [`open`](DesktopAgent#open) method on the [DesktopAgent](DesktopAgent) object. - **See also:** - [`DesktopAgent.open`](DesktopAgent#open) ## `ResolveError` +Contains constants representing the errors that can be encountered when calling the [`findIntent`](DesktopAgent#findintent), [`findIntentsByContext`](DesktopAgent#findintentsbycontext), [`raiseIntent`](DesktopAgent#raiseintent) or [`raiseIntentForContext`](DesktopAgent#raiseintentforcontext) methods on the [DesktopAgent](DesktopAgent). + @@ -285,8 +287,6 @@ public static class ResolveError -Contains constants representing the errors that can be encountered when calling the [`findIntent`](DesktopAgent#findintent), [`findIntentsByContext`](DesktopAgent#findintentsbycontext), [`raiseIntent`](DesktopAgent#raiseintent) or [`raiseIntentForContext`](DesktopAgent#raiseintentforcontext) methods on the [DesktopAgent](DesktopAgent). - **See also:** - [`DesktopAgent.findIntent`](DesktopAgent#findintent) @@ -296,6 +296,8 @@ Contains constants representing the errors that can be encountered when calling ## `ResultError` +Contains constants representing the errors that can be encountered when calling the [`getResult`](DesktopAgent#findintent) method on the [IntentResolution](Metadata#intentresolution) Object. + @@ -336,14 +338,113 @@ public static class ResultError -Contains constants representing the errors that can be encountered when calling the [`getResult`](DesktopAgent#findintent) method on the [IntentResolution](Metadata#intentresolution) Object. - **See also:** - [`DesktopAgent.addIntentListener`](DesktopAgent#addintentlistener) - [`DesktopAgent.raiseIntent`](DesktopAgent#raiseintent) - [`IntentResolution`](Metadata#intentresolution) +## `AgentError` + +Contains constants representing the errors that can be encountered when calling the [`getAgent`](getAgent) function to establish connectivity to a Desktop Agent. Primarily used with web applications, but may also be used in other language +implementations. + + + + +```ts +enum AgentError { + /** Returned when connectivity to a DA cannot be established via any + * available strategy.*/ + AgentNotFound = "AgentNotFound", + + /** Returned when a browser-based Desktop agent does not conform to the FDC3 + * Web Connection Protocol.*/ + InvalidAgent = "InvalidAgent", + + /** Returned when the app fails DA identity verification.**/ + IdentityValidationFailed = "IdentityValidationFailed", + + /** Returned when the app does not pass one of the required identity + * parameters indicating an app directory record.*/ + NoAppIdentityProvided = "NoAppIdentityProvided", + + /** Returned when the DA refuses a connection from application.*/ + AccessDenied = "AccessDenied", + + /** Returned when the failover function itself, or its resolution is not the + * right type.*/ + InvalidFailover = "InvalidFailover", + + /** Returned when reestablishment of an instance via persisted StorageSession + * data fails (e.g. after a page navigation).*/ + ReestablishConnectionFailed = "ReestablishConnectionFailed" + + /** Returned when any other error or exception occurs.*/ + ErrorOnConnect ="ErrorOnConnect" +} + +``` + + + + +```csharp +public static class ResultError +{ + /// + /// Returned when connectivity to a DA cannot be established via any + /// available strategy. + /// + public static readonly string AgentNotFound = nameof(AgentNotFound); + + /// + /// Returned when a browser-based Desktop agent does not conform to the FDC3 + /// Web Connection Protocol. + /// + public static readonly string InvalidAgent = nameof(InvalidAgent); + + /// + /// Returned when the app fails DA identity verification. + /// + public static readonly string IdentityValidationFailed = nameof(IdentityValidationFailed); + + /// + /// Returned when the app does not pass one of the required identity + /// parameters indicating an app directory record. + /// + public static readonly string NoAppIdentityProvided = nameof(NoAppIdentityProvided); + + /// + /// Returned when the DA refuses a connection from application. + /// + public static readonly string AccessDenied = nameof(AccessDenied); + + /// + /// Returned when the failover function itself, or its resolution is not the + /// right type. + /// + public static readonly string InvalidFailover = nameof(InvalidFailover); + + /// + /// Returned when reestablishment of an instance via persisted StorageSession + /// data fails (e.g. after a page navigation). + /// + public static readonly string ReestablishConnectionFailed = nameof(ReestablishConnectionFailed); + + /// + /// Returned when any other error or exception occurs. + /// + public static readonly string ErrorOnConnect = nameof(ErrorOnConnect); +} +``` + + + + + + + ## `BridgingError` `@experimental` diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 1d7191a0a..c57709519 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -1,33 +1,51 @@ +--- +id: GetAgent +sidebar_label: GetAgent +title: GetAgent +--- -```Typescript +The `getAgent()` function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where the FDC3 Web Connection Protocol (WCP) is used to connect back to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. + +The function accepts a number of arguments that can be used to affect its behavior and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. + +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. Hence, applications are expected to provide details of an app directory record to help validate their identity. For more details on identity validation see Web section of the [Supported Platforms page](../supported-platforms#web). + +If no Desktop Agent is found, or an issue prevent connection to it, the `getAgent()` function will eventually reject +its promise, which apps can handle to set themselves up to run without connection to a Desktop Agent or to display an error. + +```ts /** * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports - * injection or by using the FDC3 Web Connection Protocol (WCP) to establish a connection - * with a browser-resident Desktop Agent. + * injection of the API or by using the FDC3 Web Connection Protocol (WCP) to establish + * a connection to a browser-resident Desktop Agent. + * + * If a Desktop Agent is not found initially, any `failover` function supplied as an argument + * will be run allowing an app to alternative means of connecting to a Desktop Agent, such + * as the use of a proprietary adaptor, starting on in a new window or iframe, etc. + * + * If no agent is found, via neither the default strategies or any failover function supplied, + * then `getAgent()` will reject with an error from the AgentError enumeration. * * @param {GetAgentParams} params Required parameters for the function. * * @return A promise that resolves to an object which contains a DesktopAgent, or which - * rejects with a string from the AgentError union if a DesktopAgent cannot be established. + * rejects with a string from the `AgentError` union if a DesktopAgent cannot be established. * * @example * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp@myorg.com/appd” + * appId: “myApp@myorg.com” * }); * * * @example Using appDUrl * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp”, - * appDUrl: "myorg.com/appd" + * appDUrl: "https://myorg.com/api/appd/apps/myApp" * }); */ export type GetAgentFunction = ( - params: GetAgentParams, + params: GetAgentParams, ) => Promise<{ - desktopAgent: DesktopAgent, - implementationMetadata: ImplementationMetadata, - appMetadata: AppMetadata + desktopAgent: DesktopAgent }>; /** @@ -37,7 +55,7 @@ export type GetAgentFunction = ( * @property {string} appId The fully qualified appId that represents the application. * (in the form @) * - * @property {URL} appDUrl A URL that points to an appD record for the application. + * @property {string} appDUrl A URL that points to an appD record for the application. * Used as an alternative to providing a fully qualified appId. * * @property {number} timeout Number of milliseconds to allow for establishing a @@ -45,50 +63,42 @@ export type GetAgentFunction = ( * run. Default 750. * * @property {boolean} channelSelector Flag indicating that the application - * requires getAgent() to create a channel selector UI. Defaults to true. + * requires `getAgent() `to create a channel selector UI, which may be provided + * either by the Desktop Agent or the default FDC3 implementation. Defaults to true. * * @property {boolean} intentResolver Flag indicating that the application - * requires getAgent() to create an intent resolver UI. Defaults to true. + * requires `getAgent()` to create an intent resolver UI, which may be provided + * either by the Desktop Agent or the default FDC3 implementation. Defaults to true. * * @property {function} failover A optional function that can establish connectivity - * to a DesktopAgent if standard mechanisms fail. If a WindowProxy or URL is provided - * then getAgent() will re-run its internal algorithm with those objects (restarting - * the timeout). The function may also simply return a DesktopAgent. + * to a DesktopAgent if standard mechanisms fail or otherwise modify the behavior of the + * app when no Desktop Agent is available. If a URL is provided, `getAgent()` will + * create a hidden iframe to load it into. Alternatively, the app's failover function + * may return a `WindowProxy` Object (i.e. the object returned by a `window.open()` + * call or the `contentWindow` property of an iframe). In either case, `getAgent()` + * will re-run its internal algorithm with those objects (restarting the timeout). + * The function may also simply resolve to a DesktopAgent implementation, which will be + * passed along to the app. */ - export type GetAgentParams = { timeout ?: number, // Defaults to 750 appId ?: string, appDUrl ?: string, + channelSelector ?: boolean, + intentResolver ?: boolean, failover ?: (args: GetAgentParams) => Promise }; +``` -/** - * Represents the set of errors that may be returned by getAgent() if connectivity - * cannot be established with a DesktopAgent. - * - * "AgentNotFound" - Returned when connectivity to a DA cannot be established. - * - * "InvalidAgent" - Returned when a DA does not conform to the Web Connection Protocol (WCP). - * - * "IdentityValidationFailed" - Returned when the app fails DA identity verification. - * - * "NoAppIdentityProvided" - Returned when the DA refuses a connection from application. - * - * "AccessDenied" - TODO? Returned when the app does not pass one of the required identity parameters indicating an app directory record. - * - * "InvalidFailover" - Returned when the failover function itself, or its resolution is not the right type. - * - * "ReestablishConnectionFailed" - Returned when reestablishment of an instance via persisted StorageSession data fails (e.g. after a page navigation) - * - * "ErrorOnConnect" - Returned when any other error or exception occurs. - */ +## Persisted Connection Data -export type AgentError = "AgentNotFound" | "InvalidAgent" | "IdentityValidationFailed" | "NoAppIdentityProvided" | "AccessDenied" | "ErrorOnConnect" | "InvalidFailover" | "ReestablishConnectionFailed"; +The `getAgent()` function uses SessionStorage ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), [HTML Living Standard](https://html.spec.whatwg.org/multipage/webstorage.html)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. The details persisted conform to the following type: +```ts /** * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. - * getAgent() will use this connection information when it exists to ensure a consistent instanceId. + * getAgent() will use this connection information when it exists to ensure a consistent instanceId + * and connection type. */ export type DesktopAgentDetails = { /** The type of DA. Prevents an inadvertent switch to a different agent.*/ @@ -96,7 +106,8 @@ export type DesktopAgentDetails = { /** May contain the URL that was used to connect to the prior DA. * This may have been provided by a parent window that has since - * closed. It may therefore be used to open a new window/iframe and restart the DA. */ + * closed or a failover function. It may therefore be used to open + * a new window/iframe and restart the DA. */ url?: string, /** The prior appId set by this window. If a appDUrl was previously set then @@ -119,7 +130,7 @@ export type DesktopAgentDetails = { /** Specifies the means by which a connection to the DA is made. * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. * "PARENT" - The DA runs in a parent window or iframe. - * "URL" - The DA is loaded as an iframe in the app window. + * "FAILOVER" - The DA, or details to connect to one, were provided by an app supplied failover function. */ -export type WebDesktopAgentType = "INJECTED" | "PARENT" | "URL"; -``` \ No newline at end of file +export type WebDesktopAgentType = "INJECTED" | "PARENT" | "FAILOVER"; +``` diff --git a/website/sidebars.json b/website/sidebars.json index 0b72a4b3b..67e098a30 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -16,6 +16,7 @@ "items": [ "api/spec", "api/supported-platforms", + "api/ref/GetAgent", "api/ref/DesktopAgent", "api/ref/Channel", "api/ref/PrivateChannel", From 7d975e98506afb735f813b7e465964a1b447f689 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Apr 2024 14:42:59 +0100 Subject: [PATCH 012/152] minor clarification in Private Channel content --- docs/api/ref/PrivateChannel.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/ref/PrivateChannel.md b/docs/api/ref/PrivateChannel.md index 0d1d63200..2076f0e08 100644 --- a/docs/api/ref/PrivateChannel.md +++ b/docs/api/ref/PrivateChannel.md @@ -59,7 +59,7 @@ interface IPrivateChannel : IChannel, IIntentResult ### 'Server-side' example -The intent app establishes and returns a `PrivateChannel` to the client (who is awaiting `getResult()`). When the client calls `addContextlistener()` on that channel, the intent app receives notice via the handler added with `onAddContextListener()` and knows that the client is ready to start receiving quotes. +The intent app establishes and returns a `PrivateChannel` to the client (who is awaiting `getResult()`). When the client calls `addContextlistener()` on that channel, the intent app receives notice via the handler added with `onAddContextListener()` and knows that the client is ready to start receiving quotes. If the client unsubscribes it's context listener or disconnects from the channel, the intent app may receive notice via the the `onUnsubscribe()` and `onDisconnect()` and can stop sending data and clean-up. The Desktop Agent knows that a channel is being returned by inspecting the object returned from the handler (e.g. check constructor or look for private member). @@ -130,7 +130,7 @@ _desktopAgent.AddIntentListener("QuoteStream", async (context, metad ### 'Client-side' example -The 'client' application retrieves a `Channel` by raising an intent with context and awaiting the result. It adds a `ContextListener` so that it can receive messages from it. If a `PrivateChannel` was returned this may in turn trigger a handler added on the 'server-side' with `onAddContextListener()` and start the stream. The onDisconnect() listener may also be to clean up when the 'server-side' disconnects the stream. +The 'client' application retrieves a `Channel` by raising an intent with context and awaiting the result. It adds a `ContextListener` so that it can receive messages from it. If a `PrivateChannel` was returned this may in turn trigger a handler added on the 'server-side' with `onAddContextListener()` and start the stream. The `onDisconnect()` listener may also be to clean up when the 'server-side' disconnects the stream. Although this interaction occurs entirely in frontend code, we refer to it as the 'client-side' interaction as it requests and receives a stream of responses. From 4def718ed4990a42791717600ef779b9b816a02c Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Apr 2024 15:46:16 +0100 Subject: [PATCH 013/152] moving new DA specs files --- docs/{ => api}/specs/browserCommunicationProtocol.md | 0 docs/{ => api}/specs/browserResidentDesktopAgents.md | 0 docs/{ => api}/specs/getAgent.md | 0 docs/{ => api}/specs/preloadDesktopAgents.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename docs/{ => api}/specs/browserCommunicationProtocol.md (100%) rename docs/{ => api}/specs/browserResidentDesktopAgents.md (100%) rename docs/{ => api}/specs/getAgent.md (100%) rename docs/{ => api}/specs/preloadDesktopAgents.md (100%) diff --git a/docs/specs/browserCommunicationProtocol.md b/docs/api/specs/browserCommunicationProtocol.md similarity index 100% rename from docs/specs/browserCommunicationProtocol.md rename to docs/api/specs/browserCommunicationProtocol.md diff --git a/docs/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md similarity index 100% rename from docs/specs/browserResidentDesktopAgents.md rename to docs/api/specs/browserResidentDesktopAgents.md diff --git a/docs/specs/getAgent.md b/docs/api/specs/getAgent.md similarity index 100% rename from docs/specs/getAgent.md rename to docs/api/specs/getAgent.md diff --git a/docs/specs/preloadDesktopAgents.md b/docs/api/specs/preloadDesktopAgents.md similarity index 100% rename from docs/specs/preloadDesktopAgents.md rename to docs/api/specs/preloadDesktopAgents.md From 278eca6cf5ed4f78aafdb2d91dc31f7b9edf4d64 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Apr 2024 17:34:33 +0100 Subject: [PATCH 014/152] Updates to API spec based on PR feedback --- docs/api/spec.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index f4c487dc7..0b1547478 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -51,15 +51,13 @@ The FDC3 API specification consists of interfaces. It is expected that each Des Other interfaces defined in the spec are not critical to define as concrete types. Rather, the Desktop Agent should expect to have objects of the interface shape passed into or out of their library. -FDC3 supports multiple types of desktop agents: +FDC3 Desktop Agent implementations may provide a number of different types of interface for apps to connect with the agent: -- [Preload DAs](../specs/preloadDesktopAgents.md) - DAs that use a technology that allows the `fdc3` interface to be instantiated on the global object without any action by the app itself. Example technologies that support preloading include DAs built with Electron, Webviews, and Browser Extensions. -- [Browser-resident DAs](../specs/browserResidentDesktopAgents) - DAs that run in standard browsers without the assistance of any non-standard technology. -- Native language DAs - DAs that run in native containers. +- [Web Preload](specs/preloadDesktopAgents.md) - DAs that use a technology that allows the `fdc3` interface to be instantiated on the global object (`window.fdc3`) without any action by the app itself. Example technologies that support preloading include DAs built with Electron, Webviews, and Browser Extensions. +- [Web Proxy](specs/browserResidentDesktopAgents) - DAs that run in standard browsers (and therefore can't instantiate `window.fdc3` within an application's window) may use `window.postMessage`-based Web Connection Protocol and FDC3 Wire Protocol to implement communication between an App and a Desktop Agent in different windows or frames. +- Native language adaptors - DAs that run in native containers MAY provide proprietary libraries to connect to the Desktop Agent. -Communication between different types of DA may be handled in a proprietary manner by vendors or through [Desktop Agent Bridging](#inter-agent-communication). - -For implementation details relating to particular languages, and how to access the API in those languages, please see [Supported Platforms](supported-platforms). +For implementation details relating to these interfaces and particular languages, , please see [Supported Platforms](supported-platforms). ### Standards vs. Implementation @@ -118,8 +116,8 @@ An FDC3 Standard compliant Desktop Agent implementation **MUST**: - Only require app directories that they connect to to have implemented only the minimum requirements specified in the [App Directory API Part](../app-directory/spec) of this Standard. - Provide details of whether they implement optional features of the Desktop Agent API in the `optionalFeatures` property of the [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object returned by the [`fdc3.getInfo()`](ref/DesktopAgent#getinfo) function. - Allow, by default, at least a 15 second timeout for an application, launched via [`fdc3.open`](../api/ref/DesktopAgent#open), [`fdc3.raiseIntent`](../api/ref/DesktopAgent#raiseintent) or [`fdc3.raiseIntentForContext`](../api/ref/DesktopAgent#raiseintentforcontext) to add any context listener (via [`fdc3.addContextListener`](../api/ref/DesktopAgent#addcontextlistener)) or intent listener (via [`fdc3.addIntentListener`](../api/ref/DesktopAgent#addintentlistener)) necessary to deliver context or intent and context to it on launch. This timeout only applies to listeners needed to receive context on launch; further intent and context listeners not required on launch MAY be added later. -- Implement the [Browser-Resident Desktop Agent spec](../specs/browserResidentDesktopAgents.md) if it is intended to run in a standard browser. -- Implement the [Preload Desktop Agent spec](../specs/preloadtDesktopAgents.md) if it is intended to run in a container that supports injecting a global `fdc` object. +- Implement the [Browser-Resident Desktop Agent spec](specs/browserResidentDesktopAgents.md) if it is intended to support apps running in a standard browser. +- Implement the [Preload Desktop Agent spec](specs/preloadDesktopAgents.md) if it is intended to support apps running in a container or other environment that supports injecting a global `fdc3` object. An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: @@ -131,7 +129,7 @@ An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: - Make metadata about each context message or intent and context message received (including the app that originated the message) available to the receiving application. - Prevent external apps from listening or publishing on a [`PrivateChannel`](ref/PrivateChannel) that they did not request or provide. - Enforce compliance with the expected behavior of intents (where Intents specify a contract that is enforceable by schema, for example, return object types) and return an error if the interface is not met. -- Implement validateAppIdentity() +- Implement [`validateAppIdentity()`](ref/DesktopAgent#validateappidentity). An FDC3 Standard compliant Desktop Agent implementation **MAY**: From f5366a04745425aa4af439199c63a528ca5b4f9f Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 1 May 2024 14:14:14 +0100 Subject: [PATCH 015/152] improving supported platforms content --- docs/api/supported-platforms.md | 57 +++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 674e1857a..81e105b53 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -9,20 +9,47 @@ There are two main categories of platform: web and native, both of which are des ## Web -For a web application to be FDC3-enabled, it needs to run in the context of an environment or **_Platform Provider_** that makes the FDC3 API available to the application (a "Desktop Agent" - DA). Applications use the `DesktopAgent` interface to connect to the DA (we refer to this as the `fdc3` object or the "FDC3 API"). DAs may either be "Preload DAs" (where the DA itself lives in code outside of the browser - such as an Electron app or a Browser Extension) or "Browser Resident DAs" (where the DA lives in a separate frame or window.) +For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the desktop agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). -### API Access - Connecting to FDC3 +There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): -FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc` library and then calling the provided `getAgent()` function. This will ensure that the app can run in any type of DA (either browser-resident DAs or DAs that support injection.) (Apps SHOULD NO LONGER rely on the existence of a global `fdc3` object.) +- **Preload**: Used where the desktop agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. +- **Proxy**: Used when running in standard web browser (without an extension) and the desktop agent has to run in a different window or frame to the application and must be communicated with via Cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of Cross-document messaging, allowing the application to work with the FDC3 API directly. -Apps MUST call `getAgent()` with their identity. This can be done in either of three ways. +The FDC3 Standard defines a Web Connection Protocol (WCP) that allows apps to work with either interface, by detecting which is applicable. The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. + +Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support either of the standardized interfaces. + +:::note + +In prior versions of FDC3 (<= 2.1) Apps were required to use the 'Preload' interface, i.e. they relied on the existence of the `window.fdc3` object, which meant that apps running in a standard web browser had to import libraries specific to the Desktop Agent implementation in use. From FDC3 2.2 onwards the 'Proxy' interface is available, which allows apps in a standard web browser to connect to any Desktop Agent that implements that interface. + +Hence, from FDC3 2.2 onwards apps should switch from using `window.fdc3` directly to calling the `getAgent()` function to retrieve a `DesktopAgent` API interface. + +::: + +:::tip + +To simplify migration of an app that works with `window.fdc3` to using `getAgent()`, simply set the `fdc3 property on the global object yourself, i.e.: + +```ts +getAgent({ appId: “yourApp@yourorg.org” })) +.then((fdc3: DesktopAgent) => { window.fdc3 = fdc3 }) +.catch((error) => { console.error(`Failed to retrieve FDC3 Desktop Agent: ${error}`) }); +``` + +::: + + +As Web applications can navigate (or be navigated by users) to different URLs and become different application, apps MUST pass details of their identity to `getAgent()`. This can be done in one of two ways. 1. Provide an appId field The appId SHOULD be _fully qualified_ (containing a domain name). The DA will then use this to construct a query to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/v2/apps/yourApp". Example: Obtaining an fdc3 interface - ```JavaScript + + ```js import { getAgent } from "@finos/fdc3"; try { @@ -50,7 +77,8 @@ Apps MUST call `getAgent()` with their identity. This can be done in either of t The DA will construct the AppD query according to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/appd/v2/apps/yourApp". Example: Obtaining an fdc3 interface using an AppD locator - ```JavaScript + + ```js try { const fdc3 = await getAgent({ appId: "yourApp", appDUrl: 'https://yourorg.org/appd' }); } catch (e) { @@ -67,7 +95,8 @@ Applications MAY provide additional fields related to configuration or failover Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. Example: Decreasing the timeout and providing a failover function -```JavaScript + +```js const fdc3 = await getAgent({ appId: “myApp@yourorg.org”, timeout: 250, @@ -88,18 +117,18 @@ The failover function allows an application to provide a backup mechanism for co Failover functions MUST be asynchronous MUST resolve to one of the following types: 1) DesktopAgent + The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. +2) WindowProxy (Window object) + The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. +3) URL + If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. -The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. + If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. -2) WindowProxy (Window object) -The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. -3) URL -If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. -If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. ### Usage @@ -187,7 +216,7 @@ The FDC3 Standard does not currently define wire formats for an app to communica ## Hybrid -In a hybrid application, a standalone native application incorporates a web view, within which a web application runs. This may be considered a special case of the web platform where all platform-provider requirements for web applications must be satisfied, but it is the responsibility of the associated native application, rather than a platform provider, to ensure they are fulfilled. This may be achieved, for example, by injecting an implementation of the DesktopAgent API and ensuring that it is accessible at the usual location, `window.fdc3`. +In a hybrid application, a standalone native application incorporates a web view, within which a web application runs. This may be considered a special case of the web platform where all platform-provider requirements for web applications must be satisfied, but it is the responsibility of the associated native application, rather than a platform provider, to ensure they are fulfilled. This may be achieved via either of the defined web interfaces, i.e. by injecting an implementation of the DesktopAgent API at `window.fdc3` or via the FDC3 Web Connection Protocol (`postMessage`). ## Compliance From a43c7a1f9b96966a5863d685a4600f8d80eef525 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 00:13:12 +0100 Subject: [PATCH 016/152] Updating raiseIntent API messages to include a NeedsResolution message --- schemas/api/findIntentResponse.schema.json | 3 ++- .../findIntentsByContextResponse.schema.json | 3 ++- .../raiseIntentForContextResponse.schema.json | 21 +++++++++++++++++++ schemas/api/raiseIntentResponse.schema.json | 19 +++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/schemas/api/findIntentResponse.schema.json b/schemas/api/findIntentResponse.schema.json index 19a73b7b1..914a5fd04 100644 --- a/schemas/api/findIntentResponse.schema.json +++ b/schemas/api/findIntentResponse.schema.json @@ -44,7 +44,8 @@ }, "required": [ "appIntent" - ] + ], + "additionalProperties": false }, "FindIntentErrorResponsePayload": { "title": "", diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index 0d4162128..259638bba 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -48,7 +48,8 @@ }, "required": [ "appIntents" - ] + ], + "additionalProperties": false }, "FindIntentsByContextErrorResponsePayload": { "title": "FindIntentsByContext Error Response Payload", diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json index 1ee14409a..fe8bae408 100644 --- a/schemas/api/raiseIntentForContextResponse.schema.json +++ b/schemas/api/raiseIntentForContextResponse.schema.json @@ -19,6 +19,9 @@ { "$ref": "raiseIntentResponse.schema.json#/$defs/RaiseIntentSuccessResponsePayload" }, + { + "$ref": "#/$defs/RaiseIntentForContextNeedsResolutionResponsePayload" + }, { "$ref": "raiseIntentResponse.schema.json#/$defs/RaiseIntentErrorResponsePayload" } @@ -33,6 +36,24 @@ "RaiseIntentForContextResponseType": { "title": "RaiseIntentForContext Response Message Type", "const": "raiseIntentForContextResponse" + }, + "RaiseIntentForContextNeedsResolutionResponsePayload": { + "title": "RaiseIntentForContext NeedsResolution Response Payload", + "description": "Response to a raiseIntentForContext request that needs additional resolution (i.e. show an intent resolver UI)", + "type": "object", + "properties": { + "appIntents": { + "type": "array", + "items": { + "$ref": "../api/api.schema.json#/definitions/AppIntent" + }, + "additionalProperties": false + } + }, + "required": [ + "appIntents" + ], + "additionalProperties": false } } } \ No newline at end of file diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index c0f010d57..233f885d8 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -19,6 +19,9 @@ { "$ref": "#/$defs/RaiseIntentSuccessResponsePayload" }, + { + "$ref": "#/$defs/RaiseIntentNeedsResolutionResponsePayload" + }, { "$ref": "#/$defs/RaiseIntentErrorResponsePayload" } @@ -36,6 +39,7 @@ }, "RaiseIntentSuccessResponsePayload": { "title": "RaiseIntent Response Payload", + "description": "Response to a raiseIntent request that was successfully resolved", "type": "object", "properties": { "intentResolution": { @@ -47,8 +51,23 @@ ], "additionalProperties": false }, + "RaiseIntentNeedsResolutionResponsePayload": { + "title": "RaiseIntent NeedsResolution Response Payload", + "description": "Response to a raiseIntent request that needs additional resolution (i.e. show an intent resolver UI)", + "type": "object", + "properties": { + "appIntent": { + "$ref": "../api/api.schema.json#/definitions/AppIntent" + } + }, + "required": [ + "appIntent" + ], + "additionalProperties": false + }, "RaiseIntentErrorResponsePayload": { "title": "RaiseIntent Error Response Payload", + "description": "Response to a raiseIntent request that resulted in an error", "type": "object", "properties": { "error": { From 5d45c1d0a8125aff6bf98c3c8c824762b49e0957 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 00:13:36 +0100 Subject: [PATCH 017/152] Adding Web Connection protocol messages --- .../api/specs/browserResidentDesktopAgents.md | 3 +- docs/api/supported-platforms.md | 1 - schemas/api/WCP1Hello.schema.json | 50 ++++++++++++ schemas/api/WCP2LoadUrl.schema.json | 51 +++++++++++++ schemas/api/WCP3Handshake.schema.json | 66 ++++++++++++++++ .../api/WCP4ValidateAppIdentity.schema.json | 76 +++++++++++++++++++ ...idateAppIdentityFailedResponse.schema.json | 49 ++++++++++++ ...CP5ValidateAppIdentityResponse.schema.json | 66 ++++++++++++++++ schemas/api/WCPConnectionStep.json | 50 ++++++++++++ schemas/api/common.schema.json | 5 ++ 10 files changed, 414 insertions(+), 3 deletions(-) create mode 100644 schemas/api/WCP1Hello.schema.json create mode 100644 schemas/api/WCP2LoadUrl.schema.json create mode 100644 schemas/api/WCP3Handshake.schema.json create mode 100644 schemas/api/WCP4ValidateAppIdentity.schema.json create mode 100644 schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json create mode 100644 schemas/api/WCP5ValidateAppIdentityResponse.schema.json create mode 100644 schemas/api/WCPConnectionStep.json diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 10c424c89..7582af5dd 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -48,7 +48,7 @@ window.addEventListener("message", (event) => { }); ``` -Alternatively for step 3, a DA MAY return a `url` instead transferring a MessagePort. When `url` is provided in the response, `getAgent()` will open that `url` in a hidden iframe and initiate another WCP handshake with that frame. +Alternatively for step 3, a DA MAY return a `url` instead of transferring a MessagePort. When `url` is provided in the response, `getAgent()` will open that `url` in a hidden iframe and initiate another WCP handshake with that frame. Example WCP negotiation with URL ```JavaScript @@ -170,7 +170,6 @@ const authenticateApp = (connection, e) => { type: "WCPValidateAppIdentityResponse", payload: { desktopAgentDetails: { - agentType: "PARENT", appId: data.appId, instanceUUid: connection.instanceUuid, instanceId: connection.instanceId, diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 81e105b53..b841eac19 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -40,7 +40,6 @@ getAgent({ appId: “yourApp@yourorg.org” })) ::: - As Web applications can navigate (or be navigated by users) to different URLs and become different application, apps MUST pass details of their identity to `getAgent()`. This can be done in one of two ways. 1. Provide an appId field diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json new file mode 100644 index 000000000..6eb2677a0 --- /dev/null +++ b/schemas/api/WCP1Hello.schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json", + "title": "Web Connection Protocol Hello", + "description": "Hello message sent by an application to a parent window or frame when attempting to establish connectivity to a Desktop Agent", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP1HelloBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP1HelloBase": { + "type":"object", + "properties": { + "type": { + "title": "WCP1Hello Message Type", + "const": "WCP1Hello" + }, + "payload": { + "title": "WCP1Hello Payload", + "type": "object", + "properties": { + "fdc3Version": { + "title": "FDC3 version", + "type": "string", + "description": "The version of FDC3 API that the app supports." + } + }, + "required": ["fdc3Version"] + }, + "meta": { + "title": "WCP1Hello Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": ["timestamp","connectionAttemptUuid"] + } + }, + "required": ["type", "payload", "meta"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCP2LoadUrl.schema.json b/schemas/api/WCP2LoadUrl.schema.json new file mode 100644 index 000000000..bbba5e2af --- /dev/null +++ b/schemas/api/WCP2LoadUrl.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json", + "title": "Web Connection Protocol LoadUrl", + "description": "Response from a Desktop Agent to an application requesting access to it indicating that it should load a specified URL into a hidden iframe in order to establish connectivity to a Desktop Agent", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP2LoadUrlBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP2LoadUrlBase": { + "type":"object", + "properties": { + "type": { + "title": "WCP2LoadUrl Message Type", + "const": "WCPLoadUrl" + }, + "payload": { + "title": "WCP2LoadUrl Payload", + "type": "object", + "properties": { + "iframeUrl": { + "title": "iframe URL", + "type": "string", + "description": "A URL which can be used to establish communication with the Desktop Agent, via loading the URL into an iframe and restarting the Web Connection protocol with the iframe as the target", + "format": "uri" + } + }, + "required": ["iframeUrl"] + }, + "meta": { + "title": "WCP2LoadUrl Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": ["timestamp","connectionAttemptUuid"] + } + }, + "required": ["type", "payload", "meta"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json new file mode 100644 index 000000000..e3565162d --- /dev/null +++ b/schemas/api/WCP3Handshake.schema.json @@ -0,0 +1,66 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP3Handshake.schema.json", + "title": "WCP3Handshake", + "description": "Handshake message sent by the Desktop Agent to the app with a MessagePort that should be used for subsequent communication steps.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP3HandshakeBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP3HandshakeBase": { + "type": "object", + "properties": { + "type": { + "title": "WCP3Handshake Message Type", + "const": "WCP3Handshake" + }, + "payload": { + "title": "WCP3Handshake Payload", + "type": "object", + "properties": { + "messagePort": { + "title": "MessagePort", + "type": "object", + "additionalProperties": true + }, + "fdc3Version": { + "title": "FDC3 version", + "type": "string", + "description": "The version of FDC3 API that the Desktop Agent will provide support for." + } + }, + "additionalProperties": false, + "required": [ + "messagePort", + "fdc3Version" + ] + }, + "meta": { + "title": "WCP3Handshake Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": [ + "timestamp", + "connectionAttemptUuid" + ] + } + }, + "required": [ + "type", + "payload", + "meta" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json new file mode 100644 index 000000000..1dd7c2ec0 --- /dev/null +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP4ValidateAppIdentity.schema.json", + "title": "WCP4ValidateAppIdentity", + "description": "Identity Validation request from an app attempting to connect to a Desktop Agent.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP4ValidateAppIdentityBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP4ValidateAppIdentityBase": { + "type": "object", + "properties": { + "type": { + "title": "WCP4ValidateAppIdentity Message Type", + "const": "WCP4ValidateAppIdentity" + }, + "payload": { + "title": "WCP4ValidateAppIdentity Payload", + "type": "object", + "properties": { + "appId": { + "title": "appId", + "description": "appId for the application attempting to connect. The appId must be fully qualified (appId@host.domain.appD) such that the URL for the appD record can be determined for identity validation purposes, or appDUrl must also be specified.", + "type": "string" + }, + "appDUrl": { + "title": "appDUrl", + "description": "URL for an App Directory record that provides identity details for the application attempting to connect", + "type": "string", + "format": "uri" + }, + "instanceId": { + "title": "instanceId", + "description": "If an application has previously connected to the desktop agent, it may specify its prior instance id and associated instance UUID to request the same same instance Id be assigned.", + "type": "string" + }, + "instanceUuid": { + "title": "instanceUuid", + "description": "Instance UUID associated with the requested instanceId.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "appId" + ] + }, + "meta": { + "title": "WCP4ValidateAppIdentity Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": [ + "timestamp", + "connectionAttemptUuid" + ] + } + }, + "required": [ + "type", + "payload", + "meta" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json new file mode 100644 index 000000000..ac62e5b51 --- /dev/null +++ b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP5ValidateAppIdentityFailedResponse.schema.json", + "title": "WCP5ValidateAppIdentityFailedResponse", + "description": "Message sent by the Desktop Agent to an app if their identity validation fails.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP5ValidateAppIdentityFailedResponseBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP5ValidateAppIdentityFailedResponseBase": { + "type":"object", + "properties": { + "type": { + "title": "WCP5ValidateAppIdentityFailedResponse Message Type", + "const": "WCP5ValidateAppIdentityFailedResponse" + }, + "payload": { + "title": "WCP5ValidateAppIdentityFailedResponse Payload", + "type": "object", + "properties": { + "message": { + "title": "Identity Validation failed message", + "type": "string" + } + }, + "additionalProperties": false + }, + "meta": { + "title": "WCP5ValidateAppIdentityFailedResponse Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": ["timestamp","connectionAttemptUuid"] + } + }, + "required": ["type", "meta"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json new file mode 100644 index 000000000..2b16db35e --- /dev/null +++ b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json @@ -0,0 +1,66 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP5ValidateAppIdentityResponse.schema.json", + "title": "WCP5ValidateAppIdentityFailedResponse", + "description": "Message sent by the Desktop Agent to an app after successful identity validation", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP5ValidateAppIdentityResponseBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP5ValidateAppIdentityResponseBase": { + "type":"object", + "properties": { + "type": { + "title": "WCP5ValidateAppIdentityResponse Message Type", + "const": "WCP5ValidateAppIdentityResponse" + }, + "payload": { + "title": "WCP5ValidateAppIdentityResponse Payload", + "type": "object", + "properties": { + "appId": { + "title": "appId", + "description": "The appId that the app's identity was validated against", + "type": "string" + }, + "instanceId": { + "title": "instanceId", + "description": "The instance Id granted to the application by the Desktop Agent.", + "type": "string" + }, + "instanceUuid": { + "title": "instanceUuid", + "description": "Instance UUID associated with the instanceId granted, which may be used to retrieve the same instance Id if the app is reloaded or navigates.", + "type": "string" + }, + "implementationMetadata": { + "title": "ImplementationMetadata", + "description": "Implementation metadata for the Desktop Agent, which includes an appMetadata element containing a copy of the app's own metadata", + "$ref": "api.schema.json#/ImplementationMetadata" + } + }, + "additionalProperties": false, + "required": ["appId","instanceId","instanceUuid", "implementationMetadata"] + }, + "meta": { + "title": "WCP5ValidateAppIdentityResponse Metadata", + "type": "object", + "properties": { + "timestamp": true, + "connectionAttemptUuid": true + }, + "additionalProperties": false, + "required": ["timestamp","connectionAttemptUuid"] + } + }, + "required": ["type", "meta"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCPConnectionStep.json b/schemas/api/WCPConnectionStep.json new file mode 100644 index 000000000..46f4f546c --- /dev/null +++ b/schemas/api/WCPConnectionStep.json @@ -0,0 +1,50 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/WCPConnectionStep.schema.json", + "title": "Web Connection Protocol Message", + "type": "object", + "description": "A message used during the connection flow for an application to a Desktop Agent in a browser window. Used for messages sent in either direction.", + "properties": { + "type": { + "title": "Connection Step Message type", + "type": "string", + "enum": [ + "WCP1Hello", + "WCP2LoadUrl", + "WCP3Handshake", + "WCP4ValidateAppIdentity", + "WCP5ValidateAppIdentityFailedResponse", + "WCP5ValidateAppIdentityResponse" + ], + "description": "Identifies the type of the connection step message." + }, + "payload": { + "title": "Message payload", + "type": "object", + "description": "The message payload, containing data pertaining to this connection step.", + "unevaluatedProperties": false + }, + "meta": { + "$ref": "#/$defs/ConnectionStepMeta" + } + }, + "required": ["type", "payload", "meta"], + "additionalProperties": false, + "$defs": { + "ConnectionStepMeta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message.", + "type": "object", + "properties": { + "connectionAttemptUuid": { + "$ref": "../api/common.schema.json#/$defs/ConnectionAttemptUuid" + }, + "timestamp": { + "$ref": "../api/common.schema.json#/$defs/Timestamp" + } + }, + "required": ["timestamp"], + "additionalProperties": false + } + } +} diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json index 901b36b6b..072d29b40 100644 --- a/schemas/api/common.schema.json +++ b/schemas/api/common.schema.json @@ -5,6 +5,11 @@ "type": "object", "description": "Common definitions that are referenced in the API and Bridging wire protocol schemas", "$defs": { + "ConnectionAttemptUuid": { + "title": "Connection Attempt UUID", + "type": "string", + "description": "Unique identifier for a for an attempt to connect to a Desktop Agent" + }, "RequestUuid": { "title": "Request UUID", "type": "string", From cdd5737ce7ccb8a01158c7fa121ed8149a7c0848 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 00:50:00 +0100 Subject: [PATCH 018/152] Corrections to API schemas from testing --- package-lock.json | 4 +- package.json | 1 + s2tQuicktypeUtil.js | 2 +- .../addContextListenerResponse.schema.json | 7 +-- .../api/addIntentListenerResponse.schema.json | 2 +- schemas/api/agentResponseMessage.schema.json | 45 ++++++++++++------- schemas/api/api.schema.json | 2 +- schemas/api/common.schema.json | 10 ++--- .../createPrivateChannelResponse.schema.json | 4 +- schemas/api/findInstancesRequest.schema.json | 2 +- schemas/api/findInstancesResponse.schema.json | 16 +++---- schemas/api/findIntentRequest.schema.json | 10 ++--- schemas/api/findIntentResponse.schema.json | 6 +-- .../findIntentsByContextResponse.schema.json | 6 +-- schemas/api/getAppMetadataRequest.schema.json | 2 +- .../api/getAppMetadataResponse.schema.json | 6 +-- .../api/getCurrentChannelResponse.schema.json | 2 +- .../getOrCreateChannelResponse.schema.json | 4 +- .../api/getUserChannelsResponse.schema.json | 4 +- .../api/joinUserChannelsResponse.schema.json | 2 +- .../leaveCurrentChannelResponse.schema.json | 2 +- schemas/api/openRequest.schema.json | 2 +- schemas/api/openResponse.schema.json | 6 +-- ...ivateChannelDisconnectResponse.schema.json | 2 +- ...hanneladdEventListenerResponse.schema.json | 2 +- .../raiseIntentForContextRequest.schema.json | 2 +- .../raiseIntentForContextResponse.schema.json | 2 +- schemas/api/raiseIntentRequest.schema.json | 2 +- schemas/api/raiseIntentResponse.schema.json | 8 ++-- .../api/raiseIntentResultResponse.schema.json | 2 +- 30 files changed, 91 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2bb9b37d9..bc8bcca0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@finos/fdc3", - "version": "2.1.0-beta.9", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@finos/fdc3", - "version": "2.1.0-beta.9", + "version": "2.1.0", "license": "Apache-2.0", "devDependencies": { "@rollup/plugin-commonjs": "^25.0.7", diff --git a/package.json b/package.json index 126d87cab..89bf64ce3 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "lint": "eslint src/ test/ --ext .ts --fix", "prepack": "npm run build", "typegen": "node s2tQuicktypeUtil.js schemas/context src/context/ContextTypes.ts", + "typegen-browser": "node s2tQuicktypeUtil.js schemas/api src/api/BrowserTypes.ts", "typegen-bridging": "node s2tQuicktypeUtil.js schemas/api schemas/bridging schemas/context/context.schema.json src/bridging/BridgingTypes.ts" }, "husky": { diff --git a/s2tQuicktypeUtil.js b/s2tQuicktypeUtil.js index 6f5672328..fb7261a5c 100644 --- a/s2tQuicktypeUtil.js +++ b/s2tQuicktypeUtil.js @@ -44,7 +44,7 @@ while (dirIndex < inputs.length) { //const quicktypeExec = "node " + ["..","quicktype","dist","index.js"].join(path.sep); const quicktypeExec = ['.', 'node_modules', '.bin', 'quicktype'].join(path.sep); -const command = `${quicktypeExec} --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${sources}`; +const command = `${quicktypeExec} --debug all --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${sources}`; console.log('command to run: ' + command); exec(command, function(error, stdout, stderr) { diff --git a/schemas/api/addContextListenerResponse.schema.json b/schemas/api/addContextListenerResponse.schema.json index 04a4efd66..6cf1de4bf 100644 --- a/schemas/api/addContextListenerResponse.schema.json +++ b/schemas/api/addContextListenerResponse.schema.json @@ -44,14 +44,15 @@ }, "required": [ "listenerUUID" - ] + ], + "additionalProperties": false }, "AddContextListenerErrorResponsePayload": { - "title": "", + "title": "AddContextListener Error Response Payload", "type": "object", "properties": { "error": { - "$ref": "../api/api.schema.json#/definitions/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/addIntentListenerResponse.schema.json b/schemas/api/addIntentListenerResponse.schema.json index 7a2f7ddd8..3e6b8b38a 100644 --- a/schemas/api/addIntentListenerResponse.schema.json +++ b/schemas/api/addIntentListenerResponse.schema.json @@ -51,7 +51,7 @@ "type": "object", "properties": { "error": { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" } }, "required": [ diff --git a/schemas/api/agentResponseMessage.schema.json b/schemas/api/agentResponseMessage.schema.json index 67125df4e..62e02f263 100644 --- a/schemas/api/agentResponseMessage.schema.json +++ b/schemas/api/agentResponseMessage.schema.json @@ -39,20 +39,25 @@ "title": "Agent Response Message Payload", "type": "object", "description": "A payload for a response to an API call that will contain any return values or an `error` property containing a standardized error message indicating that the request was unsuccessful.", - "oneOf": [{ - "type": "object", - "properties": {}, - "additionalProperties": true - },{ - "type": "object", - "properties": { - "error": { - "$ref": "common.schema.json#/$defs/ErrorMessages" - } + "oneOf": [ + { + "type": "object", + "properties": {}, + "additionalProperties": true }, - "required": ["success","error"], - "additionalProperties": false - }] + { + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/$defs/ErrorMessages" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + ] }, "meta": { "title": "Agent Response Message Metadata", @@ -74,10 +79,18 @@ "$ref": "api.schema.json#/definitions/AppIdentifier" } }, - "required": ["timestamp", "requestUuid", "responseUuid"], + "required": [ + "timestamp", + "requestUuid", + "responseUuid" + ], "additionalProperties": false } }, "additionalProperties": false, - "required": ["type", "payload", "meta"] -} + "required": [ + "type", + "payload", + "meta" + ] +} \ No newline at end of file diff --git a/schemas/api/api.schema.json b/schemas/api/api.schema.json index 959e14a30..49ea4c22b 100644 --- a/schemas/api/api.schema.json +++ b/schemas/api/api.schema.json @@ -104,7 +104,7 @@ "instanceMetadata": { "description": "An optional set of, implementation specific, metadata fields that can be used to disambiguate instances, such as a window title or screen position. Must only be set if `instanceId` is set.", "type": "object", - "additionalProperties": {}, + "additionalProperties": true, "title": "instanceMetadata" }, "title": { diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json index 072d29b40..a8720aa32 100644 --- a/schemas/api/common.schema.json +++ b/schemas/api/common.schema.json @@ -39,19 +39,19 @@ "ErrorMessages": { "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" }, { - "$ref": "../api/api.schema.json#/definitions/OpenError" + "$ref": "api.schema.json#/definitions/OpenError" }, { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/ResultError" + "$ref": "api.schema.json#/definitions/ResultError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] }, diff --git a/schemas/api/createPrivateChannelResponse.schema.json b/schemas/api/createPrivateChannelResponse.schema.json index ee26d6eeb..7d556a60b 100644 --- a/schemas/api/createPrivateChannelResponse.schema.json +++ b/schemas/api/createPrivateChannelResponse.schema.json @@ -39,7 +39,7 @@ "type": "object", "properties": { "privateChannel": { - "$ref": "../api/api.schema.json#/definitions/Channel" + "$ref": "api.schema.json#/definitions/Channel" } }, "required": [ @@ -52,7 +52,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/findInstancesRequest.schema.json b/schemas/api/findInstancesRequest.schema.json index 3a7247057..3876dee37 100644 --- a/schemas/api/findInstancesRequest.schema.json +++ b/schemas/api/findInstancesRequest.schema.json @@ -32,7 +32,7 @@ "title": "FindInstances Request Payload", "properties": { "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": ["app"], diff --git a/schemas/api/findInstancesResponse.schema.json b/schemas/api/findInstancesResponse.schema.json index 61758dba4..ca7335d26 100644 --- a/schemas/api/findInstancesResponse.schema.json +++ b/schemas/api/findInstancesResponse.schema.json @@ -17,10 +17,10 @@ "payload": { "oneOf": [ { - "$ref": "$defs/FindInstancesSuccessResponsePayload" + "$ref": "#/$defs/FindInstancesSuccessResponsePayload" }, { - "$ref": "$defs/FindInstancesErrorResponsePayload" + "$ref": "#/$defs/FindInstancesErrorResponsePayload" } ] }, @@ -35,14 +35,14 @@ "const": "findInstancesResponse" }, "FindInstancesSuccessResponsePayload": { - "title": "Agent Response Message Payload", + "title": "FindInstances Response Message Payload", "type": "object", "description": "The message payload contains a flag indicating whether the API call was successful, plus any return values for the FDC3 API function called, or indicating that the request resulted in an error and including a standardized error message.", "properties": { "appIdentifiers": { "type": "array", "items": { - "$ref": "../api/api.schema.json#/definitions/AppMetadata" + "$ref": "api.schema.json#/definitions/AppMetadata" } } }, @@ -52,18 +52,18 @@ "additionalProperties": false }, "FindInstancesErrorResponsePayload": { - "title": "", + "title": "FindInstances Error Response Message Payload", "type": "object", "properties": { "error": { "title": "findInstances Errors", - "type": "String", + "type": "string", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/findIntentRequest.schema.json b/schemas/api/findIntentRequest.schema.json index 88e06ec13..3bdfc6550 100644 --- a/schemas/api/findIntentRequest.schema.json +++ b/schemas/api/findIntentRequest.schema.json @@ -42,12 +42,12 @@ "resultType": { "title": "Result type argument", "type": "string" - }, - "required": [ - "intent" - ] + } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "intent" + ] } } } \ No newline at end of file diff --git a/schemas/api/findIntentResponse.schema.json b/schemas/api/findIntentResponse.schema.json index 914a5fd04..cda85bd49 100644 --- a/schemas/api/findIntentResponse.schema.json +++ b/schemas/api/findIntentResponse.schema.json @@ -39,7 +39,7 @@ "type": "object", "properties": { "appIntent": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" + "$ref": "api.schema.json#/definitions/AppIntent" } }, "required": [ @@ -56,10 +56,10 @@ "type": "String", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index 259638bba..fee7bfec8 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -41,7 +41,7 @@ "appIntents": { "type": "array", "items": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" + "$ref": "api.schema.json#/definitions/AppIntent" }, "additionalProperties": false } @@ -60,10 +60,10 @@ "type": "String", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/getAppMetadataRequest.schema.json b/schemas/api/getAppMetadataRequest.schema.json index d670335c6..eb51811c7 100644 --- a/schemas/api/getAppMetadataRequest.schema.json +++ b/schemas/api/getAppMetadataRequest.schema.json @@ -32,7 +32,7 @@ "type": "object", "properties": { "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": [ diff --git a/schemas/api/getAppMetadataResponse.schema.json b/schemas/api/getAppMetadataResponse.schema.json index 7aeceba35..55dc8fdaa 100644 --- a/schemas/api/getAppMetadataResponse.schema.json +++ b/schemas/api/getAppMetadataResponse.schema.json @@ -39,7 +39,7 @@ "type": "object", "properties": { "appMetadata": { - "$ref": "../api/api.schema.json#/definitions/AppMetadata" + "$ref": "api.schema.json#/definitions/AppMetadata" } }, "required": [ @@ -56,10 +56,10 @@ "type": "String", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/getCurrentChannelResponse.schema.json b/schemas/api/getCurrentChannelResponse.schema.json index 87714a122..c2d7619d0 100644 --- a/schemas/api/getCurrentChannelResponse.schema.json +++ b/schemas/api/getCurrentChannelResponse.schema.json @@ -40,7 +40,7 @@ "properties": { "channel": { "oneOf": [ - { "$ref": "../api/api.schema.json#/definitions/Channel" }, + { "$ref": "api.schema.json#/definitions/Channel" }, { "type": "null" } ] } diff --git a/schemas/api/getOrCreateChannelResponse.schema.json b/schemas/api/getOrCreateChannelResponse.schema.json index 4ab59df7e..6ef69294d 100644 --- a/schemas/api/getOrCreateChannelResponse.schema.json +++ b/schemas/api/getOrCreateChannelResponse.schema.json @@ -39,7 +39,7 @@ "type": "object", "properties": { "channel": { - "$ref": "../api/api.schema.json#/definitions/Channel" + "$ref": "api.schema.json#/definitions/Channel" } }, "required": [ @@ -52,7 +52,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/getUserChannelsResponse.schema.json b/schemas/api/getUserChannelsResponse.schema.json index 76bcce75c..18db259b4 100644 --- a/schemas/api/getUserChannelsResponse.schema.json +++ b/schemas/api/getUserChannelsResponse.schema.json @@ -41,7 +41,7 @@ "userChannels": { "type": "array", "items": { - "$ref": "../api/api.schema.json#/definitions/Channel" + "$ref": "api.schema.json#/definitions/Channel" } } }, @@ -55,7 +55,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/joinUserChannelsResponse.schema.json b/schemas/api/joinUserChannelsResponse.schema.json index 7d7afa875..d6b9bdea6 100644 --- a/schemas/api/joinUserChannelsResponse.schema.json +++ b/schemas/api/joinUserChannelsResponse.schema.json @@ -45,7 +45,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/leaveCurrentChannelResponse.schema.json b/schemas/api/leaveCurrentChannelResponse.schema.json index e0bc33e22..29e3b9b2c 100644 --- a/schemas/api/leaveCurrentChannelResponse.schema.json +++ b/schemas/api/leaveCurrentChannelResponse.schema.json @@ -45,7 +45,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/openRequest.schema.json b/schemas/api/openRequest.schema.json index 77c9ba036..9a50758bf 100644 --- a/schemas/api/openRequest.schema.json +++ b/schemas/api/openRequest.schema.json @@ -32,7 +32,7 @@ "type": "object", "properties": { "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": [ diff --git a/schemas/api/openResponse.schema.json b/schemas/api/openResponse.schema.json index e71d7e6ed..352002d47 100644 --- a/schemas/api/openResponse.schema.json +++ b/schemas/api/openResponse.schema.json @@ -39,7 +39,7 @@ "type": "object", "properties": { "appIdentifier": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": [ @@ -56,10 +56,10 @@ "type": "String", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/OpenError" + "$ref": "api.schema.json#/definitions/OpenError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/privateChannelDisconnectResponse.schema.json b/schemas/api/privateChannelDisconnectResponse.schema.json index 99d54c57d..8d6a7613a 100644 --- a/schemas/api/privateChannelDisconnectResponse.schema.json +++ b/schemas/api/privateChannelDisconnectResponse.schema.json @@ -45,7 +45,7 @@ "type": "object", "properties": { "error": { - "$ref": "api.schema.json#/$defs/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/privateChanneladdEventListenerResponse.schema.json b/schemas/api/privateChanneladdEventListenerResponse.schema.json index f33847bd5..ed7447c1e 100644 --- a/schemas/api/privateChanneladdEventListenerResponse.schema.json +++ b/schemas/api/privateChanneladdEventListenerResponse.schema.json @@ -51,7 +51,7 @@ "type": "object", "properties": { "error": { - "$ref": "../api/api.schema.json#/definitions/ChannelError" + "$ref": "api.schema.json#/definitions/ChannelError" } }, "required": [ diff --git a/schemas/api/raiseIntentForContextRequest.schema.json b/schemas/api/raiseIntentForContextRequest.schema.json index 758f9dbaf..da56d04f4 100644 --- a/schemas/api/raiseIntentForContextRequest.schema.json +++ b/schemas/api/raiseIntentForContextRequest.schema.json @@ -35,7 +35,7 @@ "$ref": "../context/context.schema.json" }, "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": [ diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json index fe8bae408..0e2ca06a2 100644 --- a/schemas/api/raiseIntentForContextResponse.schema.json +++ b/schemas/api/raiseIntentForContextResponse.schema.json @@ -45,7 +45,7 @@ "appIntents": { "type": "array", "items": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" + "$ref": "api.schema.json#/definitions/AppIntent" }, "additionalProperties": false } diff --git a/schemas/api/raiseIntentRequest.schema.json b/schemas/api/raiseIntentRequest.schema.json index a0ce69d40..9833b2417 100644 --- a/schemas/api/raiseIntentRequest.schema.json +++ b/schemas/api/raiseIntentRequest.schema.json @@ -38,7 +38,7 @@ "$ref": "../context/context.schema.json" }, "app": { - "$ref": "../api/api.schema.json#/definitions/AppIdentifier" + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "required": [ diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index 233f885d8..4112d607a 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -43,7 +43,7 @@ "type": "object", "properties": { "intentResolution": { - "$ref": "../api/api.schema.json#/definitions/IntentResolution" + "$ref": "api.schema.json#/definitions/IntentResolution" } }, "required": [ @@ -57,7 +57,7 @@ "type": "object", "properties": { "appIntent": { - "$ref": "../api/api.schema.json#/definitions/AppIntent" + "$ref": "api.schema.json#/definitions/AppIntent" } }, "required": [ @@ -75,10 +75,10 @@ "type": "String", "oneOf": [ { - "$ref": "../api/api.schema.json#/definitions/ResolveError" + "$ref": "api.schema.json#/definitions/ResolveError" }, { - "$ref": "../api/api.schema.json#/definitions/BridgingError" + "$ref": "api.schema.json#/definitions/BridgingError" } ] } diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index 7ac244b87..3cd97e088 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -59,7 +59,7 @@ "title": "IntentResult Channel", "properties": { "channel": { - "$ref": "../api/api.schema.json#/definitions/Channel" + "$ref": "api.schema.json#/definitions/Channel" } }, "required": [ From eebb133bfdcc4a21706eaf5175f323c67e7393ff Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 12:45:52 +0100 Subject: [PATCH 019/152] fixing further minor schema errors --- schemas/api/WCP1Hello.schema.json | 6 +++--- schemas/api/WCPConnectionStep.json | 6 +++--- schemas/api/findIntentResponse.schema.json | 1 - schemas/api/findIntentsByContextResponse.schema.json | 1 - schemas/api/getAppMetadataResponse.schema.json | 1 - schemas/api/openResponse.schema.json | 1 - schemas/api/privateChannelDisconnectRequest.schema.json | 4 ++-- .../api/privateChanneladdEventListenerRequest.schema.json | 6 +++--- schemas/api/raiseIntentResponse.schema.json | 1 - schemas/api/raiseIntentResultResponse.schema.json | 3 +-- 10 files changed, 12 insertions(+), 18 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 6eb2677a0..e0fe2c6bc 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -14,7 +14,7 @@ ], "$defs": { "WCP1HelloBase": { - "type":"object", + "type": "object", "properties": { "type": { "title": "WCP1Hello Message Type", @@ -26,8 +26,8 @@ "properties": { "fdc3Version": { "title": "FDC3 version", - "type": "string", - "description": "The version of FDC3 API that the app supports." + "description": "The version of FDC3 API that the app supports.", + "type": "string" } }, "required": ["fdc3Version"] diff --git a/schemas/api/WCPConnectionStep.json b/schemas/api/WCPConnectionStep.json index 46f4f546c..06bb0c1c0 100644 --- a/schemas/api/WCPConnectionStep.json +++ b/schemas/api/WCPConnectionStep.json @@ -37,13 +37,13 @@ "type": "object", "properties": { "connectionAttemptUuid": { - "$ref": "../api/common.schema.json#/$defs/ConnectionAttemptUuid" + "$ref": "common.schema.json#/$defs/ConnectionAttemptUuid" }, "timestamp": { - "$ref": "../api/common.schema.json#/$defs/Timestamp" + "$ref": "common.schema.json#/$defs/Timestamp" } }, - "required": ["timestamp"], + "required": ["timestamp","connectionAttemptUuid"], "additionalProperties": false } } diff --git a/schemas/api/findIntentResponse.schema.json b/schemas/api/findIntentResponse.schema.json index cda85bd49..6dab9e4d4 100644 --- a/schemas/api/findIntentResponse.schema.json +++ b/schemas/api/findIntentResponse.schema.json @@ -53,7 +53,6 @@ "properties": { "error": { "title": "findIntent Errors", - "type": "String", "oneOf": [ { "$ref": "api.schema.json#/definitions/ResolveError" diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index fee7bfec8..787671f67 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -57,7 +57,6 @@ "properties": { "error": { "title": "FindIntentsByContext Error Message", - "type": "String", "oneOf": [ { "$ref": "api.schema.json#/definitions/ResolveError" diff --git a/schemas/api/getAppMetadataResponse.schema.json b/schemas/api/getAppMetadataResponse.schema.json index 55dc8fdaa..4efecd3fa 100644 --- a/schemas/api/getAppMetadataResponse.schema.json +++ b/schemas/api/getAppMetadataResponse.schema.json @@ -53,7 +53,6 @@ "properties": { "error": { "title": "GetAppMetadata Error Message", - "type": "String", "oneOf": [ { "$ref": "api.schema.json#/definitions/ResolveError" diff --git a/schemas/api/openResponse.schema.json b/schemas/api/openResponse.schema.json index 352002d47..607115894 100644 --- a/schemas/api/openResponse.schema.json +++ b/schemas/api/openResponse.schema.json @@ -53,7 +53,6 @@ "properties": { "error": { "title": "Open Error Response Payload", - "type": "String", "oneOf": [ { "$ref": "api.schema.json#/definitions/OpenError" diff --git a/schemas/api/privateChannelDisconnectRequest.schema.json b/schemas/api/privateChannelDisconnectRequest.schema.json index 758005479..0c0986764 100644 --- a/schemas/api/privateChannelDisconnectRequest.schema.json +++ b/schemas/api/privateChannelDisconnectRequest.schema.json @@ -23,11 +23,11 @@ } ], "$defs": { - "PrivateChannel.disconnectRequestType": { + "PrivateChannelDisconnectRequestType": { "title": "PrivateChannelDisconnect Request Message Type", "const": "privateChannelDisconnectRequest" }, - "PrivateChannel.disconnectRequestPayload": { + "PrivateChannelDisconnectRequestPayload": { "title": "PrivateChannelDisconnect Request Payload", "type": "object", "properties": { diff --git a/schemas/api/privateChanneladdEventListenerRequest.schema.json b/schemas/api/privateChanneladdEventListenerRequest.schema.json index f45cf69ea..a3d7e582d 100644 --- a/schemas/api/privateChanneladdEventListenerRequest.schema.json +++ b/schemas/api/privateChanneladdEventListenerRequest.schema.json @@ -6,7 +6,7 @@ "description": "An event message from the Desktop Agent to an app indicating that another app has added a context listener to a specific PrivateChannel.", "allOf": [ { - "$ref": "agentRequest.schema.json" + "$ref": "appRequest.schema.json" }, { "type": "object", @@ -32,9 +32,9 @@ "type": "object", "properties": { "privateChannelId": { - "type": "string", "title": "Private Channel Id", - "description": "The Id of the PrivateChannel that the listener should be added to." + "description": "The Id of the PrivateChannel that the listener should be added to.", + "type": "string" }, "contextType": { "title": "Context type", diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index 4112d607a..98950a356 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -72,7 +72,6 @@ "properties": { "error": { "title": "RaiseIntent Error Response Payload", - "type": "String", "oneOf": [ { "$ref": "api.schema.json#/definitions/ResolveError" diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index 3cd97e088..fa091079d 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -83,10 +83,9 @@ }, "RaiseIntentResultErrorResponsePayload": { "title": "RaiseIntentResult Error Response Payload", - "type": "object", "properties": { "error": { - "$ref": "common.schema.json#/ErrorMessages" + "$ref": "common.schema.json#/$defs/ErrorMessages" } }, "required": [ From 6c5d5aa396684e755f41eeca0ee1b8f189a0dcbb Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 14:21:44 +0100 Subject: [PATCH 020/152] failed attempts to resolve an issue with compiling the WCPConnectionStep schema --- schemas/api/WCPConnectionStep.json | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/schemas/api/WCPConnectionStep.json b/schemas/api/WCPConnectionStep.json index 06bb0c1c0..9bdc76b19 100644 --- a/schemas/api/WCPConnectionStep.json +++ b/schemas/api/WCPConnectionStep.json @@ -22,18 +22,22 @@ "title": "Message payload", "type": "object", "description": "The message payload, containing data pertaining to this connection step.", - "unevaluatedProperties": false + "additionalProperties": true }, "meta": { "$ref": "#/$defs/ConnectionStepMeta" } }, - "required": ["type", "payload", "meta"], + "required": [ + "type", + "payload", + "meta" + ], "additionalProperties": false, "$defs": { "ConnectionStepMeta": { "title": "Connection Step Metadata", - "description": "Metadata for this connection step message.", + "description": "Metadata for this connection step message", "type": "object", "properties": { "connectionAttemptUuid": { @@ -43,8 +47,11 @@ "$ref": "common.schema.json#/$defs/Timestamp" } }, - "required": ["timestamp","connectionAttemptUuid"], + "required": [ + "timestamp", + "connectionAttemptUuid" + ], "additionalProperties": false } } -} +} \ No newline at end of file From 47af92df8f24ac8e1e723826923b1518c34ddc89 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 14 May 2024 18:34:26 +0100 Subject: [PATCH 021/152] Further corrections to API schemas --- schemas/api/WCP1Hello.schema.json | 21 ++++---- schemas/api/WCP2LoadUrl.schema.json | 23 ++++---- schemas/api/WCP3Handshake.schema.json | 54 +++++++++++-------- .../api/WCP4ValidateAppIdentity.schema.json | 16 +----- ...idateAppIdentityFailedResponse.schema.json | 18 +++---- ...CP5ValidateAppIdentityResponse.schema.json | 29 +++++----- ...tep.json => WCPConnectionStep.schema.json} | 0 ...n => addIntentListenerRequest.schema.json} | 0 .../api/joinUserChannelsResponse.schema.json | 4 +- 9 files changed, 78 insertions(+), 87 deletions(-) rename schemas/api/{WCPConnectionStep.json => WCPConnectionStep.schema.json} (100%) rename schemas/api/{addIntentListenerRequest.scheam.json => addIntentListenerRequest.schema.json} (100%) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index e0fe2c6bc..7c96ccdc7 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -28,20 +28,21 @@ "title": "FDC3 version", "description": "The version of FDC3 API that the app supports.", "type": "string" + }, + "resolver": { + "title": "Intent Resolver Required", + "description": "A flag that may be used to indicate that an intent resolver is or is not required. Set to false if no intents, or only targeted intents, are raised", + "type": "boolean" + }, + "channelSelector": { + "title": "Channel Selector Required", + "description": "A flag that may be used to indicate that a channel selector UI is or is not required. If the app includes its own UI for displaying ", + "type": "boolean" } }, "required": ["fdc3Version"] }, - "meta": { - "title": "WCP1Hello Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": ["timestamp","connectionAttemptUuid"] - } + "meta": true }, "required": ["type", "payload", "meta"], "additionalProperties": false diff --git a/schemas/api/WCP2LoadUrl.schema.json b/schemas/api/WCP2LoadUrl.schema.json index bbba5e2af..eb40a9dd6 100644 --- a/schemas/api/WCP2LoadUrl.schema.json +++ b/schemas/api/WCP2LoadUrl.schema.json @@ -14,7 +14,7 @@ ], "$defs": { "WCP2LoadUrlBase": { - "type":"object", + "type": "object", "properties": { "type": { "title": "WCP2LoadUrl Message Type", @@ -31,20 +31,17 @@ "format": "uri" } }, - "required": ["iframeUrl"] + "required": [ + "iframeUrl" + ] }, - "meta": { - "title": "WCP2LoadUrl Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": ["timestamp","connectionAttemptUuid"] - } + "meta": true }, - "required": ["type", "payload", "meta"], + "required": [ + "type", + "payload", + "meta" + ], "additionalProperties": false } } diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json index e3565162d..6ce6c2dcc 100644 --- a/schemas/api/WCP3Handshake.schema.json +++ b/schemas/api/WCP3Handshake.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP3Handshake.schema.json", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json", "title": "WCP3Handshake", - "description": "Handshake message sent by the Desktop Agent to the app with a MessagePort that should be used for subsequent communication steps.", + "description": "Handshake message sent by the Desktop Agent to the app (with a MessagePort appended) that should be used for subsequent communication steps.", "type": "object", "allOf": [ { @@ -24,36 +24,46 @@ "title": "WCP3Handshake Payload", "type": "object", "properties": { - "messagePort": { - "title": "MessagePort", - "type": "object", - "additionalProperties": true - }, "fdc3Version": { "title": "FDC3 version", "type": "string", "description": "The version of FDC3 API that the Desktop Agent will provide support for." + }, + "resolver": { + "title": "Resolver URL", + "description": "Indicates whether an intent resolver UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent will handle another way)", + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "boolean" + } + ] + }, + "channelSelector": { + "title": "Channel Selector URL", + "description": "Indicates whether a channel selector UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the channel selector (as the Desktop Agent will handle another way)", + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "boolean" + } + ] } }, "additionalProperties": false, "required": [ - "messagePort", - "fdc3Version" + "fdc3Version", + "resolver", + "channelSelector" ] }, - "meta": { - "title": "WCP3Handshake Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": [ - "timestamp", - "connectionAttemptUuid" - ] - } + "meta": true }, "required": [ "type", diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json index 1dd7c2ec0..87bb0bd90 100644 --- a/schemas/api/WCP4ValidateAppIdentity.schema.json +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP4ValidateAppIdentity.schema.json", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json", "title": "WCP4ValidateAppIdentity", "description": "Identity Validation request from an app attempting to connect to a Desktop Agent.", "type": "object", @@ -51,19 +51,7 @@ "appId" ] }, - "meta": { - "title": "WCP4ValidateAppIdentity Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": [ - "timestamp", - "connectionAttemptUuid" - ] - } + "meta": true }, "required": [ "type", diff --git a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json index ac62e5b51..867534431 100644 --- a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json @@ -14,7 +14,7 @@ ], "$defs": { "WCP5ValidateAppIdentityFailedResponseBase": { - "type":"object", + "type": "object", "properties": { "type": { "title": "WCP5ValidateAppIdentityFailedResponse Message Type", @@ -31,18 +31,12 @@ }, "additionalProperties": false }, - "meta": { - "title": "WCP5ValidateAppIdentityFailedResponse Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": ["timestamp","connectionAttemptUuid"] - } + "meta": true }, - "required": ["type", "meta"], + "required": [ + "type", + "meta" + ], "additionalProperties": false } } diff --git a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json index 2b16db35e..697abb343 100644 --- a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP5ValidateAppIdentityResponse.schema.json", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json", "title": "WCP5ValidateAppIdentityFailedResponse", "description": "Message sent by the Desktop Agent to an app after successful identity validation", "type": "object", @@ -14,7 +14,7 @@ ], "$defs": { "WCP5ValidateAppIdentityResponseBase": { - "type":"object", + "type": "object", "properties": { "type": { "title": "WCP5ValidateAppIdentityResponse Message Type", @@ -42,24 +42,23 @@ "implementationMetadata": { "title": "ImplementationMetadata", "description": "Implementation metadata for the Desktop Agent, which includes an appMetadata element containing a copy of the app's own metadata", - "$ref": "api.schema.json#/ImplementationMetadata" + "$ref": "api.schema.json#/definitions/ImplementationMetadata" } }, "additionalProperties": false, - "required": ["appId","instanceId","instanceUuid", "implementationMetadata"] + "required": [ + "appId", + "instanceId", + "instanceUuid", + "implementationMetadata" + ] }, - "meta": { - "title": "WCP5ValidateAppIdentityResponse Metadata", - "type": "object", - "properties": { - "timestamp": true, - "connectionAttemptUuid": true - }, - "additionalProperties": false, - "required": ["timestamp","connectionAttemptUuid"] - } + "meta": true }, - "required": ["type", "meta"], + "required": [ + "type", + "meta" + ], "additionalProperties": false } } diff --git a/schemas/api/WCPConnectionStep.json b/schemas/api/WCPConnectionStep.schema.json similarity index 100% rename from schemas/api/WCPConnectionStep.json rename to schemas/api/WCPConnectionStep.schema.json diff --git a/schemas/api/addIntentListenerRequest.scheam.json b/schemas/api/addIntentListenerRequest.schema.json similarity index 100% rename from schemas/api/addIntentListenerRequest.scheam.json rename to schemas/api/addIntentListenerRequest.schema.json diff --git a/schemas/api/joinUserChannelsResponse.schema.json b/schemas/api/joinUserChannelsResponse.schema.json index d6b9bdea6..082f3a5fb 100644 --- a/schemas/api/joinUserChannelsResponse.schema.json +++ b/schemas/api/joinUserChannelsResponse.schema.json @@ -37,7 +37,9 @@ "JoinUserChannelSuccessResponsePayload": { "title": "JoinUserChannel Response Payload", "type": "object", - "properties": {}, + "properties": { + + }, "additionalProperties": false }, "JoinUserChannelErrorResponsePayload": { From ae9e50daa1122eb2face4a2e321704ea81d8c612 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 15 May 2024 00:29:32 +0100 Subject: [PATCH 022/152] Further schema fixes + first generated typescript output --- package.json | 2 +- s2tQuicktypeUtil.js | 18 +- schemas/api/README.md | 4 +- schemas/api/WCP2LoadUrl.schema.json | 2 +- ...idateAppIdentityFailedResponse.schema.json | 2 +- .../addContextListenerResponse.schema.json | 2 +- .../api/addIntentListenerResponse.schema.json | 4 +- ...age.schema.json => agentEvent.schema.json} | 14 +- ....schema.json => agentResponse.schema.json} | 20 +- schemas/api/appRequest.schema.json | 14 +- schemas/api/broadcastResponse.schema.json | 2 +- ...a.json => channelChangedEvent.schema.json} | 2 +- ...extListenerUnsubscribeResponse.schema.json | 2 +- .../createPrivateChannelResponse.schema.json | 2 +- schemas/api/findInstancesResponse.schema.json | 2 +- schemas/api/findIntentResponse.schema.json | 4 +- .../findIntentsByContextResponse.schema.json | 2 +- .../api/getAppMetadataResponse.schema.json | 2 +- .../api/getCurrentChannelResponse.schema.json | 2 +- schemas/api/getInfoResponse.schema.json | 2 +- .../getOrCreateChannelResponse.schema.json | 2 +- .../api/getUserChannelsResponse.schema.json | 2 +- ...entListenerUnsubscribeResponse.schema.json | 2 +- ...on => joinUserChannelResponse.schema.json} | 2 +- .../leaveCurrentChannelResponse.schema.json | 2 +- schemas/api/openResponse.schema.json | 2 +- ...ivateChannelDisconnectResponse.schema.json | 2 +- ...annelOnAddContextListenerEvent.schema.json | 2 +- ...rivateChannelOnDisconnectEvent.schema.json | 2 +- ...ivateChannelOnUnsubscribeEvent.schema.json | 2 +- ...subscribeEventListenerResponse.schema.json | 2 +- ...hanneladdEventListenerResponse.schema.json | 3 +- .../raiseIntentForContextResponse.schema.json | 2 +- schemas/api/raiseIntentResponse.schema.json | 2 +- .../api/raiseIntentResultResponse.schema.json | 2 +- src/api/BrowserTypes.ts | 4291 +++++++++++++++++ 36 files changed, 4365 insertions(+), 61 deletions(-) rename schemas/api/{agentEventMessage.schema.json => agentEvent.schema.json} (79%) rename schemas/api/{agentResponseMessage.schema.json => agentResponse.schema.json} (91%) rename schemas/api/{channelChangedEvent..schema.json => channelChangedEvent.schema.json} (96%) rename schemas/api/{joinUserChannelsResponse.schema.json => joinUserChannelResponse.schema.json} (96%) create mode 100644 src/api/BrowserTypes.ts diff --git a/package.json b/package.json index 89bf64ce3..0194a400c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "lint": "eslint src/ test/ --ext .ts --fix", "prepack": "npm run build", "typegen": "node s2tQuicktypeUtil.js schemas/context src/context/ContextTypes.ts", - "typegen-browser": "node s2tQuicktypeUtil.js schemas/api src/api/BrowserTypes.ts", + "typegen-browser": "node s2tQuicktypeUtil.js schemas/api/api.schema.json schemas/api/common.schema.json schemas/api/appRequest.schema.json schemas/api/agentResponse.schema.json schemas/api/agentEvent.schema.json schemas/api src/api/BrowserTypes.ts", "typegen-bridging": "node s2tQuicktypeUtil.js schemas/api schemas/bridging schemas/context/context.schema.json src/bridging/BridgingTypes.ts" }, "husky": { diff --git a/s2tQuicktypeUtil.js b/s2tQuicktypeUtil.js index fb7261a5c..3feaac711 100644 --- a/s2tQuicktypeUtil.js +++ b/s2tQuicktypeUtil.js @@ -25,15 +25,27 @@ let sources = ''; let dirIndex = 0; +//skip duplicate paths (we might want to specify some files to go first, and might duplicate them) +const paths = new Set(); +const addAPath = (aPath,paths,sources) => { + if (!paths.has(aPath)) { + paths.add(aPath) + return sources + ` --src ${aPath}`; + } else { + console.log(`skipping duplicate path ${aPath}`); + return sources; + } +} + while (dirIndex < inputs.length) { if (inputs[dirIndex].endsWith('.schema.json')) { - sources += `--src ${path.join(inputs[dirIndex])} `; + sources = addAPath(path.join(inputs[dirIndex]),paths,sources); } else { fs.readdirSync(inputs[dirIndex], { withFileTypes: true }).forEach(file => { if (file.isDirectory()) { inputs.push(path.join(inputs[dirIndex], file.name)); } else if (file.name.endsWith('.schema.json')) { - sources += `--src ${path.join(inputs[dirIndex], file.name)} `; + sources = addAPath(path.join(inputs[dirIndex], file.name),paths,sources); } }); } @@ -44,7 +56,7 @@ while (dirIndex < inputs.length) { //const quicktypeExec = "node " + ["..","quicktype","dist","index.js"].join(path.sep); const quicktypeExec = ['.', 'node_modules', '.bin', 'quicktype'].join(path.sep); -const command = `${quicktypeExec} --debug all --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${sources}`; +const command = `${quicktypeExec} --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${sources}`; console.log('command to run: ' + command); exec(command, function(error, stdout, stderr) { diff --git a/schemas/api/README.md b/schemas/api/README.md index 274b47798..65ba8c51f 100644 --- a/schemas/api/README.md +++ b/schemas/api/README.md @@ -12,8 +12,8 @@ Contents: - `api\schemas\api.schema.json` - partially auto-generated schema from the existing `src\api` types and metadata objects. - `api\schemas\common.schema.json` - common element definitions referenced in multiple other schemas in both the API and Bridging API protocols. - `api\schemas\appRequest.schema.json` - The base message definition that requests from an app to the DA are derived from. -- `api\schemas\agentResponseMessage.schema.json` - The base message definition that API call response messages from a DA to an app are derived from. -- `api\schemas\agentEventMessage.schema.json` - The base message definition that event messages from a DA to an app are derived from. +- `api\schemas\agentResponse.schema.json` - The base message definition that API call response messages from a DA to an app are derived from. +- `api\schemas\agentEvent.schema.json` - The base message definition that event messages from a DA to an app are derived from. - `api\schemas\*Request.schema.json` - Schemas defining request messages sent from apps to Desktop Agents. - `api\schemas\*Response.schema.json` - Schemas defining responses from DAs to apps for request messages (sent from apps to Desktop Agents). - `api\schemas\*Event.schema.json` - Schemas defining event messages sent from Desktop Agents to Apps. diff --git a/schemas/api/WCP2LoadUrl.schema.json b/schemas/api/WCP2LoadUrl.schema.json index eb40a9dd6..a8e2d582b 100644 --- a/schemas/api/WCP2LoadUrl.schema.json +++ b/schemas/api/WCP2LoadUrl.schema.json @@ -18,7 +18,7 @@ "properties": { "type": { "title": "WCP2LoadUrl Message Type", - "const": "WCPLoadUrl" + "const": "WCP2LoadUrl" }, "payload": { "title": "WCP2LoadUrl Payload", diff --git a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json index 867534431..5744b25a8 100644 --- a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/bridging/WCP5ValidateAppIdentityFailedResponse.schema.json", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json", "title": "WCP5ValidateAppIdentityFailedResponse", "description": "Message sent by the Desktop Agent to an app if their identity validation fails.", "type": "object", diff --git a/schemas/api/addContextListenerResponse.schema.json b/schemas/api/addContextListenerResponse.schema.json index 6cf1de4bf..d4addabc1 100644 --- a/schemas/api/addContextListenerResponse.schema.json +++ b/schemas/api/addContextListenerResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a addContextListener request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/addIntentListenerResponse.schema.json b/schemas/api/addIntentListenerResponse.schema.json index 3e6b8b38a..1bc9b6203 100644 --- a/schemas/api/addIntentListenerResponse.schema.json +++ b/schemas/api/addIntentListenerResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a addIntentListener request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", @@ -47,7 +47,7 @@ ] }, "AddIntentListenerErrorResponsePayload": { - "title": "", + "title": "AddIntentListener Response Error Payload", "type": "object", "properties": { "error": { diff --git a/schemas/api/agentEventMessage.schema.json b/schemas/api/agentEvent.schema.json similarity index 79% rename from schemas/api/agentEventMessage.schema.json rename to schemas/api/agentEvent.schema.json index 4d083918c..12129637b 100644 --- a/schemas/api/agentEventMessage.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -1,29 +1,29 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/agentEventMessage.schema.json", - "title": "FDC3 Agent Event Message", + "$id": "https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json", + "title": "Agent Event Message", "type": "object", "description": "A message from a Desktop Agent to an FDC3-enabled app representing an event.", "properties": { "type": { - "title": "Message Type", + "title": "Event Message Type", "type": "string", "enum": [ + "channelChangedEvent", "privateChannelOnAddContextListenerEvent", - "privateChannelOnUnsubscribeEvent", "privateChannelOnDisconnectEvent", - "channelChangedEvent" + "privateChannelOnUnsubscribeEvent" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." }, "payload": { - "title": "Agent Event Message Payload", + "title": "Event Payload", "type": "object", "description": "The message payload contains details of the event that the app is being notified about.", "additionalProperties": true }, "meta": { - "title": "Agent Event Message Metadata", + "title": "Event Metadata", "description": "Metadata for messages sent by a Desktop Agent to an App notifying it of an event.", "type": "object", "properties": { diff --git a/schemas/api/agentResponseMessage.schema.json b/schemas/api/agentResponse.schema.json similarity index 91% rename from schemas/api/agentResponseMessage.schema.json rename to schemas/api/agentResponse.schema.json index 62e02f263..4796e8afa 100644 --- a/schemas/api/agentResponseMessage.schema.json +++ b/schemas/api/agentResponse.schema.json @@ -1,17 +1,18 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/agentResponseMessage.schema.json", - "title": "FDC3 Agent Response Message", + "$id": "https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json", + "title": "Agent Response Message", "type": "object", "description": "A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the payload contains an `error` property, the request was unsuccessful.", "properties": { "type": { - "title": "Message Type", + "title": "Response Message Type", "type": "string", "enum": [ "addContextListenerResponse", "addIntentListenerResponse", "broadcastResponse", + "contextListenerUnsubscribeResponse", "createPrivateChannelResponse", "findInstancesResponse", "findIntentResponse", @@ -21,22 +22,21 @@ "getInfoResponse", "getOrCreateChannelResponse", "getUserChannelsResponse", + "intentListenerUnsubscribeResponse", "joinUserChannelResponse", "leaveCurrentChannelResponse", "openResponse", - "raiseIntentResponse", - "raiseIntentForContextResponse", - "raiseIntentResultResponse", - "contextListenerUnsubscribeResponse", - "intentListenerUnsubscribeResponse", "privateChannelAddEventListenerResponse", + "privateChannelDisconnectResponse", "privateChannelUnsubscribeEventListenerResponse", - "privateChannelDisconnectResponse" + "raiseIntentForContextResponse", + "raiseIntentResponse", + "raiseIntentResultResponse" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." }, "payload": { - "title": "Agent Response Message Payload", + "title": "Response Payload", "type": "object", "description": "A payload for a response to an API call that will contain any return values or an `error` property containing a standardized error message indicating that the request was unsuccessful.", "oneOf": [ diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index 56a9ba6ff..9420c9cf6 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/appRequest.schema.json", - "title": "FDC3 App Request Message", + "title": "App Request Message", "type": "object", "description": "A request message from an FDC3-enabled app to a Desktop Agent.", "properties": { @@ -12,6 +12,7 @@ "addContextListenerRequest", "addIntentListenerRequest", "broadcastRequest", + "contextListenerUnsubscribeRequest", "createPrivateChannelRequest", "findInstancesRequest", "findIntentRequest", @@ -21,21 +22,20 @@ "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", + "intentListenerUnsubscribeRequest", "joinUserChannelRequest", "leaveCurrentChannelRequest", "openRequest", - "raiseIntentRequest", - "raiseIntentForContextRequest", - "contextListenerUnsubscribeRequest", - "intentListenerUnsubscribeRequest", "privateChannelAddEventListenerRequest", + "privateChannelDisconnectRequest", "privateChannelUnsubscribeEventListenerRequest", - "privateChannelDisconnectRequest" + "raiseIntentForContextRequest", + "raiseIntentRequest" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Request' appended." }, "payload": { - "title": "Message payload", + "title": "Request payload", "type": "object", "description": "The message payload typically contains the arguments to FDC3 API functions." }, diff --git a/schemas/api/broadcastResponse.schema.json b/schemas/api/broadcastResponse.schema.json index 3231a4da6..6b6a38583 100644 --- a/schemas/api/broadcastResponse.schema.json +++ b/schemas/api/broadcastResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a request to broadcast context on a channel.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/channelChangedEvent..schema.json b/schemas/api/channelChangedEvent.schema.json similarity index 96% rename from schemas/api/channelChangedEvent..schema.json rename to schemas/api/channelChangedEvent.schema.json index 7bc4d5cf3..5181c20cf 100644 --- a/schemas/api/channelChangedEvent..schema.json +++ b/schemas/api/channelChangedEvent.schema.json @@ -6,7 +6,7 @@ "description": "An event message from the Desktop Agent to an app indicating that its current user channel has changed.", "allOf": [ { - "$ref": "agentEventMessage.schema.json" + "$ref": "agentEvent.schema.json" }, { "type": "object", diff --git a/schemas/api/contextListenerUnsubscribeResponse.schema.json b/schemas/api/contextListenerUnsubscribeResponse.schema.json index 30beb4150..0d47c6043 100644 --- a/schemas/api/contextListenerUnsubscribeResponse.schema.json +++ b/schemas/api/contextListenerUnsubscribeResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a request to a contextListenerUnsubscribe request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/createPrivateChannelResponse.schema.json b/schemas/api/createPrivateChannelResponse.schema.json index 7d556a60b..357538c5d 100644 --- a/schemas/api/createPrivateChannelResponse.schema.json +++ b/schemas/api/createPrivateChannelResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a createPrivateChannel request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/findInstancesResponse.schema.json b/schemas/api/findInstancesResponse.schema.json index ca7335d26..ec387c52a 100644 --- a/schemas/api/findInstancesResponse.schema.json +++ b/schemas/api/findInstancesResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a findInstances request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/findIntentResponse.schema.json b/schemas/api/findIntentResponse.schema.json index 6dab9e4d4..0e8d7416d 100644 --- a/schemas/api/findIntentResponse.schema.json +++ b/schemas/api/findIntentResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a findIntent request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", @@ -48,7 +48,7 @@ "additionalProperties": false }, "FindIntentErrorResponsePayload": { - "title": "", + "title": "FindIntent Response Error Payload", "type": "object", "properties": { "error": { diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index 787671f67..7ce0819cd 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a findIntentsByContext request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/getAppMetadataResponse.schema.json b/schemas/api/getAppMetadataResponse.schema.json index 4efecd3fa..ac9d6c16e 100644 --- a/schemas/api/getAppMetadataResponse.schema.json +++ b/schemas/api/getAppMetadataResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a getAppMetadata request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/getCurrentChannelResponse.schema.json b/schemas/api/getCurrentChannelResponse.schema.json index c2d7619d0..312f3e174 100644 --- a/schemas/api/getCurrentChannelResponse.schema.json +++ b/schemas/api/getCurrentChannelResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a getCurrentChannel request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/getInfoResponse.schema.json b/schemas/api/getInfoResponse.schema.json index bf44798a1..a10a7749d 100644 --- a/schemas/api/getInfoResponse.schema.json +++ b/schemas/api/getInfoResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a getInfo request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/getOrCreateChannelResponse.schema.json b/schemas/api/getOrCreateChannelResponse.schema.json index 6ef69294d..d887c8277 100644 --- a/schemas/api/getOrCreateChannelResponse.schema.json +++ b/schemas/api/getOrCreateChannelResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a getOrCreateChannel request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/getUserChannelsResponse.schema.json b/schemas/api/getUserChannelsResponse.schema.json index 18db259b4..ad352f21d 100644 --- a/schemas/api/getUserChannelsResponse.schema.json +++ b/schemas/api/getUserChannelsResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a getUserChannels request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/intentListenerUnsubscribeResponse.schema.json b/schemas/api/intentListenerUnsubscribeResponse.schema.json index 8396c87df..aeaec0587 100644 --- a/schemas/api/intentListenerUnsubscribeResponse.schema.json +++ b/schemas/api/intentListenerUnsubscribeResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a request to a intentListenerUnsubscribe request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/joinUserChannelsResponse.schema.json b/schemas/api/joinUserChannelResponse.schema.json similarity index 96% rename from schemas/api/joinUserChannelsResponse.schema.json rename to schemas/api/joinUserChannelResponse.schema.json index 082f3a5fb..5f49ad9e8 100644 --- a/schemas/api/joinUserChannelsResponse.schema.json +++ b/schemas/api/joinUserChannelResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a joinUserChannel request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/leaveCurrentChannelResponse.schema.json b/schemas/api/leaveCurrentChannelResponse.schema.json index 29e3b9b2c..9dc9d0938 100644 --- a/schemas/api/leaveCurrentChannelResponse.schema.json +++ b/schemas/api/leaveCurrentChannelResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a leaveCurrentChannel request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/openResponse.schema.json b/schemas/api/openResponse.schema.json index 607115894..c5ea42bbb 100644 --- a/schemas/api/openResponse.schema.json +++ b/schemas/api/openResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a open request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChannelDisconnectResponse.schema.json b/schemas/api/privateChannelDisconnectResponse.schema.json index 8d6a7613a..56eabb6e1 100644 --- a/schemas/api/privateChannelDisconnectResponse.schema.json +++ b/schemas/api/privateChannelDisconnectResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a privateChannelDisconnect request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChannelOnAddContextListenerEvent.schema.json b/schemas/api/privateChannelOnAddContextListenerEvent.schema.json index f158e48a8..3b713bc9e 100644 --- a/schemas/api/privateChannelOnAddContextListenerEvent.schema.json +++ b/schemas/api/privateChannelOnAddContextListenerEvent.schema.json @@ -6,7 +6,7 @@ "description": "An event message from the Desktop Agent to an app indicating that another app has added a context listener to a specific PrivateChannel.", "allOf": [ { - "$ref": "agentEventMessage.schema.json" + "$ref": "agentEvent.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChannelOnDisconnectEvent.schema.json b/schemas/api/privateChannelOnDisconnectEvent.schema.json index b5f6ad649..9a0432ad7 100644 --- a/schemas/api/privateChannelOnDisconnectEvent.schema.json +++ b/schemas/api/privateChannelOnDisconnectEvent.schema.json @@ -6,7 +6,7 @@ "description": "An event message from the Desktop Agent to an app indicating that another app has disconnected from a specific PrivateChannel and will no longer interact with it.", "allOf": [ { - "$ref": "agentEventMessage.schema.json" + "$ref": "agentEvent.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json index b4ee0e01a..c2a93a21c 100644 --- a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json +++ b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json @@ -6,7 +6,7 @@ "description": "An event message from the Desktop Agent to an app indicating that another app has unsubscribed a context listener from a specific PrivateChannel.", "allOf": [ { - "$ref": "agentEventMessage.schema.json" + "$ref": "agentEvent.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json b/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json index 019331b29..327a72c24 100644 --- a/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json +++ b/schemas/api/privateChannelUnsubscribeEventListenerResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a privateChannelUnsubscribeEventListener request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/privateChanneladdEventListenerResponse.schema.json b/schemas/api/privateChanneladdEventListenerResponse.schema.json index ed7447c1e..d75411f43 100644 --- a/schemas/api/privateChanneladdEventListenerResponse.schema.json +++ b/schemas/api/privateChanneladdEventListenerResponse.schema.json @@ -7,6 +7,7 @@ "allOf": [ { "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", @@ -47,7 +48,7 @@ ] }, "PrivateChannelAddEventListenerErrorResponsePayload": { - "title": "", + "title": "PrivateChannelAddEventListener Error Response Payload", "type": "object", "properties": { "error": { diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json index 0e2ca06a2..8496449ee 100644 --- a/schemas/api/raiseIntentForContextResponse.schema.json +++ b/schemas/api/raiseIntentForContextResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a raiseIntentForContext request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index 98950a356..8c0a5ef15 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -6,7 +6,7 @@ "description": "A response to a raiseIntent request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index fa091079d..9bb7b6925 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -6,7 +6,7 @@ "description": "A secondary response to a request to raise an intent used to deliver the intent result.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" + "$ref": "agentResponse.schema.json" }, { "type": "object", diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts new file mode 100644 index 000000000..5693f3e85 --- /dev/null +++ b/src/api/BrowserTypes.ts @@ -0,0 +1,4291 @@ +// To parse this data: +// +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// +// const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); +// const commonDefinitions = Convert.toCommonDefinitions(json); +// const appRequestMessage = Convert.toAppRequestMessage(json); +// const agentResponseMessage = Convert.toAgentResponseMessage(json); +// const agentEventMessage = Convert.toAgentEventMessage(json); +// const addContextListenerRequest = Convert.toAddContextListenerRequest(json); +// const addContextListenerResponse = Convert.toAddContextListenerResponse(json); +// const addIntentListenerRequest = Convert.toAddIntentListenerRequest(json); +// const addIntentListenerResponse = Convert.toAddIntentListenerResponse(json); +// const broadcastRequest = Convert.toBroadcastRequest(json); +// const broadcastResponse = Convert.toBroadcastResponse(json); +// const channelChangedEvent = Convert.toChannelChangedEvent(json); +// const contextListenerUnsubscribeRequest = Convert.toContextListenerUnsubscribeRequest(json); +// const contextListenerUnsubscribeResponse = Convert.toContextListenerUnsubscribeResponse(json); +// const createPrivateChannelRequest = Convert.toCreatePrivateChannelRequest(json); +// const createPrivateChannelResponse = Convert.toCreatePrivateChannelResponse(json); +// const findInstancesRequest = Convert.toFindInstancesRequest(json); +// const findInstancesResponse = Convert.toFindInstancesResponse(json); +// const findIntentRequest = Convert.toFindIntentRequest(json); +// const findIntentResponse = Convert.toFindIntentResponse(json); +// const findIntentsByContextRequest = Convert.toFindIntentsByContextRequest(json); +// const findIntentsByContextsByContextResponse = Convert.toFindIntentsByContextsByContextResponse(json); +// const getAppMetadataRequest = Convert.toGetAppMetadataRequest(json); +// const getAppMetadataResponse = Convert.toGetAppMetadataResponse(json); +// const getCurrentChannelRequest = Convert.toGetCurrentChannelRequest(json); +// const getCurrentChannelResponse = Convert.toGetCurrentChannelResponse(json); +// const getInfoRequest = Convert.toGetInfoRequest(json); +// const getInfoResponse = Convert.toGetInfoResponse(json); +// const getOrCreateChannelRequest = Convert.toGetOrCreateChannelRequest(json); +// const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); +// const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); +// const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const intentListenerUnsubscribeRequest = Convert.toIntentListenerUnsubscribeRequest(json); +// const intentListenerUnsubscribeResponse = Convert.toIntentListenerUnsubscribeResponse(json); +// const joinUserChannelRequest = Convert.toJoinUserChannelRequest(json); +// const joinUserChannelResponse = Convert.toJoinUserChannelResponse(json); +// const leaveCurrentChannelRequest = Convert.toLeaveCurrentChannelRequest(json); +// const leaveCurrentChannelResponse = Convert.toLeaveCurrentChannelResponse(json); +// const openRequest = Convert.toOpenRequest(json); +// const openResponse = Convert.toOpenResponse(json); +// const privateChannelAddEventListenerRequest = Convert.toPrivateChannelAddEventListenerRequest(json); +// const privateChannelAddEventListenerResponse = Convert.toPrivateChannelAddEventListenerResponse(json); +// const privateChannelDisconnectRequest = Convert.toPrivateChannelDisconnectRequest(json); +// const privateChannelDisconnectResponse = Convert.toPrivateChannelDisconnectResponse(json); +// const privateChannelOnAddContextListenerEvent = Convert.toPrivateChannelOnAddContextListenerEvent(json); +// const privateChannelOnDisconnectEvent = Convert.toPrivateChannelOnDisconnectEvent(json); +// const privateChannelOnUnsubscribeEventEvent = Convert.toPrivateChannelOnUnsubscribeEventEvent(json); +// const privateChannelUnsubscribeEventListenerRequest = Convert.toPrivateChannelUnsubscribeEventListenerRequest(json); +// const privateChannelUnsubscribeEventListenerResponse = Convert.toPrivateChannelUnsubscribeEventListenerResponse(json); +// const raiseIntentForContextRequest = Convert.toRaiseIntentForContextRequest(json); +// const raiseIntentForContextResponse = Convert.toRaiseIntentForContextResponse(json); +// const raiseIntentRequest = Convert.toRaiseIntentRequest(json); +// const raiseIntentResponse = Convert.toRaiseIntentResponse(json); +// const raiseIntentResultResponse = Convert.toRaiseIntentResultResponse(json); +// const webConnectionProtocolHello = Convert.toWebConnectionProtocolHello(json); +// const webConnectionProtocolLoadURL = Convert.toWebConnectionProtocolLoadURL(json); +// const wCP3Handshake = Convert.toWCP3Handshake(json); +// const wCP4ValidateAppIdentity = Convert.toWCP4ValidateAppIdentity(json); +// const wCP5ValidateAppIdentityFailedResponse = Convert.toWCP5ValidateAppIdentityFailedResponse(json); +// const webConnectionProtocolMessage = Convert.toWebConnectionProtocolMessage(json); +// +// These functions will throw an error if the JSON doesn't +// match the expected interface, even if the JSON is valid. + +/** + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface AppRequestMessage { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AppRequestMessageMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: { [key: string]: any }; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: RequestMessageType; +} + +/** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ +export interface AppRequestMessageMeta { + requestUuid: string; + /** + * Field that represents the source application that a request or response was received + * from. Please note that this may be set by an app or Desktop Agent proxy for debugging + * purposes but a Desktop Agent should make its own determination of the source of a message + * to avoid spoofing. + */ + source?: AppIdentifier; + timestamp: Date; +} + +/** + * Field that represents the source application that a request or response was received + * from. Please note that this may be set by an app or Desktop Agent proxy for debugging + * purposes but a Desktop Agent should make its own determination of the source of a message + * to avoid spoofing. + * + * Identifies an application, or instance of an application, and is used to target FDC3 API + * calls, such as `fdc3.open` or `fdc3.raiseIntent` at specific applications or application + * instances. + * + * Will always include at least an `appId` field, which uniquely identifies a specific app. + * + * If the `instanceId` field is set then the `AppMetadata` object represents a specific + * instance of the application that may be addressed using that Id. + * + * Field that represents the source application that the request being responded to was + * received from, for debugging purposes. + * + * Identifier for the app instance that was selected (or started) to resolve the intent. + * `source.instanceId` MUST be set, indicating the specific app instance that + * received the intent. + */ +export interface AppIdentifier { + /** + * The unique application identifier located within a specific application directory + * instance. An example of an appId might be 'app@sub.root' + */ + appId: string; + /** + * The Desktop Agent that the app is available on. Used in Desktop Agent Bridging to + * identify the Desktop Agent to target. + */ + desktopAgent?: string; + /** + * An optional instance identifier, indicating that this object represents a specific + * instance of the application described. + */ + instanceId?: string; + [property: string]: any; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ +export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; + +/** + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface AgentResponseMessage { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AgentResponseMessageMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: AgentResponseMessageResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: ResponseMessageType; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ +export interface AgentResponseMessageMeta { + requestUuid: string; + responseUuid: string; + /** + * Field that represents the source application that the request being responded to was + * received from, for debugging purposes. + */ + source?: AppIdentifier; + timestamp: Date; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface AgentResponseMessageResponsePayload { + error?: ResponsePayloadError; + [property: string]: any; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + */ +export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "IntentHandlerRejected" | "NoResultReturned" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ +export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; + +/** + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface AgentEventMessage { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: AgentEventMessageMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: { [key: string]: any }; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: EventMessageType; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ +export interface AgentEventMessageMeta { + eventUuid: string; + timestamp: Date; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ +export type EventMessageType = "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; + +/** + * A request to add a context listener to a specified Channel OR to the current user + * channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface AddContextListenerRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: AddContextListenerRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "addContextListenerRequest"; +} + +/** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ +export interface AddContextListenerRequestMeta { + requestUuid: string; + /** + * Field that represents the source application that a request or response was received + * from. Please note that this may be set by an app or Desktop Agent proxy for debugging + * purposes but a Desktop Agent should make its own determination of the source of a message + * to avoid spoofing. + */ + source?: AppIdentifier; + timestamp: Date; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface AddContextListenerRequestPayload { + /** + * The id of the channel to add the listener to or `null` indicating that it should listen + * to the current user channel (at the time of broadcast). + */ + channelId: null | string; + /** + * The type of context to listen for OR `null` indicating that it should listen to all + * context types. + */ + contextType: null | string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a addContextListener request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface AddContextListenerResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: AddContextListenerResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "addContextListenerResponse"; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ +export interface AddContextListenerResponseMeta { + requestUuid: string; + responseUuid: string; + /** + * Field that represents the source application that the request being responded to was + * received from, for debugging purposes. + */ + source?: AppIdentifier; + timestamp: Date; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface AddContextListenerResponsePayload { + error?: PurpleError; + listenerUUID?: string; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + */ +export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to add an Intent listener for a specified intent type. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface AddIntentListenerRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: AddIntentListenerRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "addIntentListenerRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface AddIntentListenerRequestPayload { + /** + * The name of the intent to listen for. + */ + intent: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a addIntentListener request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface AddIntentListenerResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: AddIntentListenerResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "addIntentListenerResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface AddIntentListenerResponsePayload { + error?: FluffyError; + listenerUUID?: string; + [property: string]: any; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + */ +export type FluffyError = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to broadcast context on a channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface BroadcastRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: BroadcastRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "broadcastRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface BroadcastRequestPayload { + /** + * The Id of the Channel that the broadcast was sent on + */ + channelId: string; + /** + * The context object that was the payload of a broadcast message. + */ + context: Context; +} + +/** + * The context object that was the payload of a broadcast message. + * + * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by + * FDC3 operations. As such, it is not really meant to be used on its own, but is imported + * by more specific type definitions (standardized or custom) to provide the structure and + * properties shared by all FDC3 context data types. + * + * The key element of FDC3 context types is their mandatory `type` property, which is used + * to identify what type of data the object represents, and what shape it has. + * + * The FDC3 context type, and all derived types, define the minimum set of fields a context + * data object of a particular type can be expected to have, but this can always be extended + * with custom fields as appropriate. + */ +export interface Context { + /** + * Context data objects may include a set of equivalent key-value pairs that can be used to + * help applications identify and look up the context type they receive in their own domain. + * The idea behind this design is that applications can provide as many equivalent + * identifiers to a target application as possible, e.g. an instrument may be represented by + * an ISIN, CUSIP or Bloomberg identifier. + * + * Identifiers do not make sense for all types of data, so the `id` property is therefore + * optional, but some derived types may choose to require at least one identifier. + * Identifier values SHOULD always be of type string. + */ + id?: { [key: string]: any }; + /** + * Context data objects may include a name property that can be used for more information, + * or display purposes. Some derived types may require the name object as mandatory, + * depending on use case. + */ + name?: string; + /** + * The type property is the only _required_ part of the FDC3 context data schema. The FDC3 + * [API](https://fdc3.finos.org/docs/api/spec) relies on the `type` property being present + * to route shared context data appropriately. + * + * FDC3 [Intents](https://fdc3.finos.org/docs/intents/spec) also register the context data + * types they support in an FDC3 [App + * Directory](https://fdc3.finos.org/docs/app-directory/overview), used for intent discovery + * and routing. + * + * Standardized FDC3 context types have well-known `type` properties prefixed with the + * `fdc3` namespace, e.g. `fdc3.instrument`. For non-standard types, e.g. those defined and + * used by a particular organization, the convention is to prefix them with an + * organization-specific namespace, e.g. `blackrock.fund`. + * + * See the [Context Data Specification](https://fdc3.finos.org/docs/context/spec) for more + * information about context data types. + */ + type: string; + [property: string]: any; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a request to broadcast context on a channel. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface BroadcastResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "broadcastResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface BroadcastResponseResponsePayload { + error?: ResponsePayloadError; + [property: string]: any; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app indicating that its current user + * channel has changed. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface ChannelChangedEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: ChannelChangedEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: ChannelChangedEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "channelChangedEvent"; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ +export interface ChannelChangedEventMeta { + eventUuid: string; + timestamp: Date; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface ChannelChangedEventPayload { + /** + * The Id of the channel that the app was added to or `null` if it was removed from a + * channel. + */ + newChannelId: null | string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to unsubscribe a context listener. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface ContextListenerUnsubscribeRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: ContextListenerUnsubscribeRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "contextListenerUnsubscribeRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface ContextListenerUnsubscribeRequestPayload { + listenerUUID: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a request to a contextListenerUnsubscribe request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface ContextListenerUnsubscribeResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "contextListenerUnsubscribeResponse"; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to return a Channel with an auto-generated identity that is intended for private + * communication between applications. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface CreatePrivateChannelRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: CreatePrivateChannelRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "createPrivateChannelRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface CreatePrivateChannelRequestPayload { +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a createPrivateChannel request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface CreatePrivateChannelResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: CreatePrivateChannelResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "createPrivateChannelResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface CreatePrivateChannelResponsePayload { + error?: PurpleError; + privateChannel?: Channel; +} + +/** + * Represents a context channel that applications can use to send and receive + * context data. + * + * Please note that There are differences in behavior when you interact with a + * User channel via the `DesktopAgent` interface and the `Channel` interface. + * Specifically, when 'joining' a User channel or adding a context listener + * when already joined to a channel via the `DesktopAgent` interface, existing + * context (matching the type of the context listener) on the channel is + * received by the context listener immediately. Whereas, when a context + * listener is added via the Channel interface, context is not received + * automatically, but may be retrieved manually via the `getCurrentContext()` + * function. + */ +export interface Channel { + /** + * Channels may be visualized and selectable by users. DisplayMetadata may be used to + * provide hints on how to see them. + * For App channels, displayMetadata would typically not be present. + */ + displayMetadata?: DisplayMetadata; + /** + * Constant that uniquely identifies this channel. + */ + id: string; + /** + * Uniquely defines each channel type. + * Can be "user", "app" or "private". + */ + type: Type; +} + +/** + * Channels may be visualized and selectable by users. DisplayMetadata may be used to + * provide hints on how to see them. + * For App channels, displayMetadata would typically not be present. + * + * A system channel will be global enough to have a presence across many apps. This gives us + * some hints + * to render them in a standard way. It is assumed it may have other properties too, but if + * it has these, + * this is their meaning. + */ +export interface DisplayMetadata { + /** + * The color that should be associated within this channel when displaying this channel in a + * UI, e.g: `0xFF0000`. + */ + color?: string; + /** + * A URL of an image that can be used to display this channel + */ + glyph?: string; + /** + * A user-readable name for this channel, e.g: `"Red"` + */ + name?: string; +} + +/** + * Uniquely defines each channel type. + * Can be "user", "app" or "private". + */ +export type Type = "app" | "private" | "user"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request for details of instances of a particular app. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface FindInstancesRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: FindInstancesRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "findInstancesRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface FindInstancesRequestPayload { + app: AppIdentifier; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a findInstances request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface FindInstancesResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: FindInstancesResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "findInstancesResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + * + * The message payload contains a flag indicating whether the API call was successful, plus + * any return values for the FDC3 API function called, or indicating that the request + * resulted in an error and including a standardized error message. + */ +export interface FindInstancesResponsePayload { + error?: FindInstancesErrors; + appIdentifiers?: AppMetadata[]; +} + +/** + * Extends an `AppIdentifier`, describing an application or instance of an application, with + * additional descriptive metadata that is usually provided by an FDC3 App Directory that + * the desktop agent connects to. + * + * The additional information from an app directory can aid in rendering UI elements, such + * as a launcher menu or resolver UI. This includes a title, description, tooltip and icon + * and screenshot URLs. + * + * Note that as `AppMetadata` instances are also `AppIdentifiers` they may be passed to the + * `app` argument of `fdc3.open`, `fdc3.raiseIntent` etc. + * + * The calling application instance's own metadata, according to the Desktop Agent (MUST + * include at least the `appId` and `instanceId`). + */ +export interface AppMetadata { + /** + * The unique application identifier located within a specific application directory + * instance. An example of an appId might be 'app@sub.root' + */ + appId: string; + /** + * A longer, multi-paragraph description for the application that could include markup + */ + description?: string; + /** + * The Desktop Agent that the app is available on. Used in Desktop Agent Bridging to + * identify the Desktop Agent to target. + */ + desktopAgent?: string; + /** + * A list of icon URLs for the application that can be used to render UI elements + */ + icons?: Icon[]; + /** + * An optional instance identifier, indicating that this object represents a specific + * instance of the application described. + */ + instanceId?: string; + /** + * An optional set of, implementation specific, metadata fields that can be used to + * disambiguate instances, such as a window title or screen position. Must only be set if + * `instanceId` is set. + */ + instanceMetadata?: { [key: string]: any }; + /** + * The 'friendly' app name. + * This field was used with the `open` and `raiseIntent` calls in FDC3 <2.0, which now + * require an `AppIdentifier` wth `appId` set. + * Note that for display purposes the `title` field should be used, if set, in preference to + * this field. + */ + name?: string; + /** + * The type of output returned for any intent specified during resolution. May express a + * particular context type (e.g. "fdc3.instrument"), channel (e.g. "channel") or a channel + * that will receive a specified type (e.g. "channel"). + */ + resultType?: null | string; + /** + * Images representing the app in common usage scenarios that can be used to render UI + * elements + */ + screenshots?: Image[]; + /** + * A more user-friendly application title that can be used to render UI elements + */ + title?: string; + /** + * A tooltip for the application that can be used to render UI elements + */ + tooltip?: string; + /** + * The Version of the application. + */ + version?: string; +} + +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright FINOS FDC3 contributors - see NOTICE file + */ +export interface Icon { + /** + * The icon dimension, formatted as `x`. + */ + size?: string; + /** + * The icon url + */ + src: string; + /** + * Icon media type. If not present the Desktop Agent may use the src file extension. + */ + type?: string; +} + +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright FINOS FDC3 contributors - see NOTICE file + */ +export interface Image { + /** + * Caption for the image. + */ + label?: string; + /** + * The image dimension, formatted as `x`. + */ + size?: string; + /** + * The image url. + */ + src: string; + /** + * Image media type. If not present the Desktop Agent may use the src file extension. + */ + type?: string; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + * + * Unique identifier for a request or event message. Required in all message types + * + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. + * + * Unique identifier for a `listener` object returned by a Desktop Agent to an app in + * response to addContextListener, addIntentListener or one of the PrivateChannel event + * listeners and used to identify it in messages (e.g. when unsubscribing). + * + * Unique identifier for a for an attempt to connect to a Desktop Agent + */ +export type FindInstancesErrors = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request for details of apps available to resolve a particular intent and context pair. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface FindIntentRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: FindIntentRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "findIntentRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface FindIntentRequestPayload { + context?: Context; + intent: string; + resultType?: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a findIntent request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface FindIntentResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: FindIntentResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "findIntentResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface FindIntentResponsePayload { + error?: FindInstancesErrors; + appIntent?: AppIntent; +} + +/** + * An interface that relates an intent to apps + */ +export interface AppIntent { + /** + * Details of applications that can resolve the intent. + */ + apps: AppMetadata[]; + /** + * Details of the intent whose relationship to resolving applications is being described. + */ + intent: IntentMetadata; +} + +/** + * Details of the intent whose relationship to resolving applications is being described. + * + * Intent descriptor + */ +export interface IntentMetadata { + /** + * Display name for the intent. + */ + displayName: string; + /** + * The unique name of the intent that can be invoked by the raiseIntent call + */ + name: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request for details of intents and apps available to resolve them for a particular + * context. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface FindIntentsByContextRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: FindIntentsByContextRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "findIntentsByContextRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface FindIntentsByContextRequestPayload { + context: Context; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a findIntentsByContext request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface FindIntentsByContextsByContextResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: FindIntentsByContextsByContextResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "findIntentsByContextResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface FindIntentsByContextsByContextResponsePayload { + error?: FindInstancesErrors; + appIntents?: AppIntent[]; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request for metadata about an app. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetAppMetadataRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetAppMetadataRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getAppMetadataRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetAppMetadataRequestPayload { + app: AppIdentifier; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getAppMetadata request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetAppMetadataResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetAppMetadataResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getAppMetadataResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetAppMetadataResponsePayload { + error?: FindInstancesErrors; + appMetadata?: AppMetadata; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to return the Channel object for the current User channel membership. Returns + * `null` if the app is not joined to a channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetCurrentChannelRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetCurrentChannelRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getCurrentChannelRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetCurrentChannelRequestPayload { +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getCurrentChannel request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetCurrentChannelResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetCurrentChannelResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getCurrentChannelResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetCurrentChannelResponsePayload { + error?: ResponsePayloadError; + channel?: Channel | null; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to retrieve information about the FDC3 Desktop Agent implementation and the + * metadata of the calling application according to the desktop agent. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetInfoRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetInfoRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getInfoRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetInfoRequestPayload { +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getInfo request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetInfoResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetInfoResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getInfoResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetInfoResponsePayload { + error?: ResponsePayloadError; + implementationMetadata?: ImplementationMetadata; +} + +/** + * Implementation metadata for the Desktop Agent, which includes an appMetadata element + * containing a copy of the app's own metadata + * + * Includes Metadata for the current application. + * + * Metadata relating to the FDC3 Desktop Agent implementation and its provider. + */ +export interface ImplementationMetadata { + /** + * The calling application instance's own metadata, according to the Desktop Agent (MUST + * include at least the `appId` and `instanceId`). + */ + appMetadata: AppMetadata; + /** + * The version number of the FDC3 specification that the implementation provides. + * The string must be a numeric semver version, e.g. 1.2 or 1.2.1. + */ + fdc3Version: string; + /** + * Metadata indicating whether the Desktop Agent implements optional features of + * the Desktop Agent API. + */ + optionalFeatures: OptionalFeatures; + /** + * The name of the provider of the Desktop Agent implementation (e.g. Finsemble, Glue42, + * OpenFin etc.). + */ + provider: string; + /** + * The version of the provider of the Desktop Agent implementation (e.g. 5.3.0). + */ + providerVersion?: string; +} + +/** + * Metadata indicating whether the Desktop Agent implements optional features of + * the Desktop Agent API. + */ +export interface OptionalFeatures { + /** + * Used to indicate whether the experimental Desktop Agent Bridging + * feature is implemented by the Desktop Agent. + */ + DesktopAgentBridging: boolean; + /** + * Used to indicate whether the exposure of 'originating app metadata' for + * context and intent messages is supported by the Desktop Agent. + */ + OriginatingAppMetadata: boolean; + /** + * Used to indicate whether the optional `fdc3.joinUserChannel`, + * `fdc3.getCurrentChannel` and `fdc3.leaveCurrentChannel` are implemented by + * the Desktop Agent. + */ + UserChannelMembershipAPIs: boolean; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to return a Channel with an auto-generated identity that is intended for private + * communication between applications. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetOrCreateChannelRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetOrCreateChannelRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getOrCreateChannelRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetOrCreateChannelRequestPayload { + /** + * The id of the channel to return + */ + channelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getOrCreateChannel request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetOrCreateChannelResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetOrCreateChannelResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getOrCreateChannelResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetOrCreateChannelResponsePayload { + error?: PurpleError; + channel?: Channel; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to retrieve a list of the User Channels available for the app to join. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetUserChannelsRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetUserChannelsRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getUserChannelsRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetUserChannelsRequestPayload { +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getUserChannels request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetUserChannelsResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetUserChannelsResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getUserChannelsResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetUserChannelsResponsePayload { + error?: PurpleError; + userChannels?: Channel[]; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to unsubscribe a context listener. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface IntentListenerUnsubscribeRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: IntentListenerUnsubscribeRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "intentListenerUnsubscribeRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface IntentListenerUnsubscribeRequestPayload { + listenerUUID: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a request to a intentListenerUnsubscribe request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface IntentListenerUnsubscribeResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "intentListenerUnsubscribeResponse"; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to join the app to the specified User channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface JoinUserChannelRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: JoinUserChannelRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "joinUserChannelRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface JoinUserChannelRequestPayload { + /** + * The id of the channel to join + */ + channelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a joinUserChannel request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface JoinUserChannelResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: JoinUserChannelResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "joinUserChannelResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface JoinUserChannelResponsePayload { + error?: PurpleError; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request to remove the app from any User channel membership. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface LeaveCurrentChannelRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: LeaveCurrentChannelRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "leaveCurrentChannelRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface LeaveCurrentChannelRequestPayload { +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a leaveCurrentChannel request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface LeaveCurrentChannelResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: LeaveCurrentChannelResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "leaveCurrentChannelResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface LeaveCurrentChannelResponsePayload { + error?: PurpleError; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to open an application. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface OpenRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: OpenRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "openRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface OpenRequestPayload { + app: AppIdentifier; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a open request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface OpenResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: OpenResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "openResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface OpenResponsePayload { + error?: OpenErrorResponsePayload; + appIdentifier?: AppIdentifier; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + */ +export type OpenErrorResponsePayload = "MalformedContext" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app indicating that another app has added a + * context listener to a specific PrivateChannel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface PrivateChannelAddEventListenerRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: TPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "privateChannelAddEventListenerRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface TPayload { + /** + * The type of the context listener to add to the channel, or null if it should listen to + * all types. + */ + contextType: null | string; + /** + * The Id of the PrivateChannel that the listener should be added to. + */ + privateChannelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a privateChannelAddEventListener request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface PrivateChannelAddEventListenerResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: PrivateChannelAddEventListenerResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelAddEventListenerResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface PrivateChannelAddEventListenerResponsePayload { + error?: PurpleError; + listenerUUID?: string; + [property: string]: any; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Request that indicates that a participant will no longer interact with a specified + * `PrivateChannel`. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface PrivateChannelDisconnectRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: PrivateChannelDisconnectRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "privateChannelDisconnectRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface PrivateChannelDisconnectRequestPayload { + /** + * The Id of the Channel that should be disconnected from + */ + channelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a privateChannelDisconnect request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface PrivateChannelDisconnectResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: PrivateChannelDisconnectResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelDisconnectResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface PrivateChannelDisconnectResponsePayload { + error?: PurpleError; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app indicating that another app has added a + * context listener to a specific PrivateChannel. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface PrivateChannelOnAddContextListenerEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: ChannelChangedEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: PrivateChannelOnAddContextListenerEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelOnAddContextListenerEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface PrivateChannelOnAddContextListenerEventPayload { + /** + * The type of the context listener added to the channel by another app, or null if it will + * listen to all types. + */ + contextType: null | string; + /** + * The Id of the PrivateChannel that the listener was added to. + */ + privateChannelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app indicating that another app has + * disconnected from a specific PrivateChannel and will no longer interact with it. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface PrivateChannelOnDisconnectEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: ChannelChangedEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: PrivateChannelOnDisconnectEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelOnDisconnectEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface PrivateChannelOnDisconnectEventPayload { + /** + * The Id of the PrivateChannel that the app has disconnected from. + */ + privateChannelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app indicating that another app has + * unsubscribed a context listener from a specific PrivateChannel. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface PrivateChannelOnUnsubscribeEventEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: ChannelChangedEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: PrivateChannelOnUnsubscribeEventEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelOnUnsubscribeEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface PrivateChannelOnUnsubscribeEventEventPayload { + /** + * The type of the context listener unsubscribed from the channel by another app, or null if + * it was listening to all types. + */ + contextType: null | string; + /** + * The Id of the PrivateChannel that the listener was unsubscribed from. + */ + privateChannelId: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to unsubscribe a context listener. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface PrivateChannelUnsubscribeEventListenerRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: PrivateChannelUnsubscribeEventListenerRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "privateChannelUnsubscribeEventListenerRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface PrivateChannelUnsubscribeEventListenerRequestPayload { + listenerUUID: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a privateChannelUnsubscribeEventListener request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface PrivateChannelUnsubscribeEventListenerResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "privateChannelUnsubscribeEventListenerResponse"; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to raise an unspecified intent for a specified context. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface RaiseIntentForContextRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: RaiseIntentForContextRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "raiseIntentForContextRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface RaiseIntentForContextRequestPayload { + app?: AppIdentifier; + context: Context; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a raiseIntentForContext request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface RaiseIntentForContextResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: RaiseIntentForContextResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "raiseIntentForContextResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + * + * Response to a raiseIntent request that was successfully resolved + * + * Response to a raiseIntentForContext request that needs additional resolution (i.e. show + * an intent resolver UI) + * + * Response to a raiseIntent request that resulted in an error + */ +export interface RaiseIntentForContextResponsePayload { + error?: FindInstancesErrors; + intentResolution?: IntentResolution; + appIntents?: AppIntent[]; +} + +/** + * IntentResolution provides a standard format for data returned upon resolving an intent. + * + * ```javascript + * //resolve a "Chain" type intent + * let resolution = await agent.raiseIntent("intentName", context); + * + * //resolve a "Client-Service" type intent with a data response or a Channel + * let resolution = await agent.raiseIntent("intentName", context); + * try { + * const result = await resolution.getResult(); + * if (result && result.broadcast) { + * console.log(`${resolution.source} returned a channel with id ${result.id}`); + * } else if (result){ + * console.log(`${resolution.source} returned data: ${JSON.stringify(result)}`); + * } else { + * console.error(`${resolution.source} didn't return data` + * } + * } catch(error) { + * console.error(`${resolution.source} returned an error: ${error}`); + * } + * + * // Use metadata about the resolving app instance to target a further intent + * await agent.raiseIntent("intentName", context, resolution.source); + * ``` + */ +export interface IntentResolution { + /** + * The intent that was raised. May be used to determine which intent the user + * chose in response to `fdc3.raiseIntentForContext()`. + */ + intent: string; + /** + * Identifier for the app instance that was selected (or started) to resolve the intent. + * `source.instanceId` MUST be set, indicating the specific app instance that + * received the intent. + */ + source: AppIdentifier; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to raise an intent for a context. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface RaiseIntentRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: RaiseIntentRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "raiseIntentRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface RaiseIntentRequestPayload { + app?: AppIdentifier; + context: Context; + intent: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a raiseIntent request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface RaiseIntentResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: RaiseIntentResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "raiseIntentResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + * + * Response to a raiseIntent request that was successfully resolved + * + * Response to a raiseIntent request that needs additional resolution (i.e. show an intent + * resolver UI) + * + * Response to a raiseIntent request that resulted in an error + */ +export interface RaiseIntentResponsePayload { + error?: FindInstancesErrors; + intentResolution?: IntentResolution; + appIntent?: AppIntent; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A secondary response to a request to raise an intent used to deliver the intent result. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface RaiseIntentResultResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: ResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "raiseIntentResultResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface ResponsePayload { + error?: ResponsePayloadError; + intentResult?: IntentResult; +} + +export interface IntentResult { + context?: Context; + channel?: Channel; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Hello message sent by an application to a parent window or frame when attempting to + * establish connectivity to a Desktop Agent + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WebConnectionProtocolHello { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: WebConnectionProtocolHelloPayload; + /** + * Identifies the type of the connection step message. + */ + type: "WCP1Hello"; +} + +/** + * Metadata for this connection step message + */ +export interface ConnectionStepMetadata { + connectionAttemptUuid: string; + timestamp: Date; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface WebConnectionProtocolHelloPayload { + /** + * A flag that may be used to indicate that a channel selector UI is or is not required. If + * the app includes its own UI for displaying + */ + channelSelector?: boolean; + /** + * The version of FDC3 API that the app supports. + */ + fdc3Version: string; + /** + * A flag that may be used to indicate that an intent resolver is or is not required. Set to + * false if no intents, or only targeted intents, are raised + */ + resolver?: boolean; + [property: string]: any; +} + +/** + * Identifies the type of the connection step message. + */ + +/** + * Response from a Desktop Agent to an application requesting access to it indicating that + * it should load a specified URL into a hidden iframe in order to establish connectivity to + * a Desktop Agent + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WebConnectionProtocolLoadURL { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: WebConnectionProtocolLoadURLPayload; + /** + * Identifies the type of the connection step message. + */ + type: "WCP2LoadUrl"; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface WebConnectionProtocolLoadURLPayload { + /** + * A URL which can be used to establish communication with the Desktop Agent, via loading + * the URL into an iframe and restarting the Web Connection protocol with the iframe as the + * target + */ + iframeUrl: string; + [property: string]: any; +} + +/** + * Identifies the type of the connection step message. + */ + +/** + * Handshake message sent by the Desktop Agent to the app (with a MessagePort appended) that + * should be used for subsequent communication steps. + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WCP3Handshake { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: WCP3HandshakePayload; + /** + * Identifies the type of the connection step message. + */ + type: "WCP3Handshake"; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface WCP3HandshakePayload { + /** + * Indicates whether a channel selector UI is required and the URL to use to do so. Set to + * `true` to use the default or `false` to disable the channel selector (as the Desktop + * Agent will handle another way) + */ + channelSelector: boolean | string; + /** + * The version of FDC3 API that the Desktop Agent will provide support for. + */ + fdc3Version: string; + /** + * Indicates whether an intent resolver UI is required and the URL to use to do so. Set to + * `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent + * will handle another way) + */ + resolver: boolean | string; +} + +/** + * Identifies the type of the connection step message. + */ + +/** + * Identity Validation request from an app attempting to connect to a Desktop Agent. + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WCP4ValidateAppIdentity { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: PayloadClass; + /** + * Identifies the type of the connection step message. + */ + type: "WCP4ValidateAppIdentity"; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface PayloadClass { + /** + * URL for an App Directory record that provides identity details for the application + * attempting to connect + */ + appDUrl?: string; + /** + * appId for the application attempting to connect. The appId must be fully qualified + * (appId@host.domain.appD) such that the URL for the appD record can be determined for + * identity validation purposes, or appDUrl must also be specified. + */ + appId: string; + /** + * If an application has previously connected to the desktop agent, it may specify its prior + * instance id and associated instance UUID to request the same same instance Id be assigned. + */ + instanceId?: string; + /** + * Instance UUID associated with the requested instanceId. + */ + instanceUuid?: string; +} + +/** + * Identifies the type of the connection step message. + */ + +/** + * Message sent by the Desktop Agent to an app after successful identity validation + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WCP5ValidateAppIdentityFailedResponse { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: WCP5ValidateAppIdentityFailedResponsePayload; + /** + * Identifies the type of the connection step message. + */ + type: "WCP5ValidateAppIdentityResponse"; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface WCP5ValidateAppIdentityFailedResponsePayload { + /** + * The appId that the app's identity was validated against + */ + appId: string; + /** + * Implementation metadata for the Desktop Agent, which includes an appMetadata element + * containing a copy of the app's own metadata + */ + implementationMetadata: ImplementationMetadata; + /** + * The instance Id granted to the application by the Desktop Agent. + */ + instanceId: string; + /** + * Instance UUID associated with the instanceId granted, which may be used to retrieve the + * same instance Id if the app is reloaded or navigates. + */ + instanceUuid: string; +} + +/** + * Identifies the type of the connection step message. + */ + +/** + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WebConnectionProtocolMessage { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: { [key: string]: any }; + /** + * Identifies the type of the connection step message. + */ + type: ConnectionStepMessageType; +} + +/** + * Identifies the type of the connection step message. + */ +export type ConnectionStepMessageType = "WCP1Hello" | "WCP2LoadUrl" | "WCP3Handshake" | "WCP4ValidateAppIdentity" | "WCP5ValidateAppIdentityFailedResponse" | "WCP5ValidateAppIdentityResponse"; + +// Converts JSON strings to/from your types +// and asserts the results of JSON.parse at runtime +export class Convert { + public static toFDC3DesktopAgentAPISchema(json: string): any { + return cast(JSON.parse(json), "any"); + } + + public static fDC3DesktopAgentAPISchemaToJson(value: any): string { + return JSON.stringify(uncast(value, "any"), null, 2); + } + + public static toCommonDefinitions(json: string): { [key: string]: any } { + return cast(JSON.parse(json), m("any")); + } + + public static commonDefinitionsToJson(value: { [key: string]: any }): string { + return JSON.stringify(uncast(value, m("any")), null, 2); + } + + public static toAppRequestMessage(json: string): AppRequestMessage { + return cast(JSON.parse(json), r("AppRequestMessage")); + } + + public static appRequestMessageToJson(value: AppRequestMessage): string { + return JSON.stringify(uncast(value, r("AppRequestMessage")), null, 2); + } + + public static toAgentResponseMessage(json: string): AgentResponseMessage { + return cast(JSON.parse(json), r("AgentResponseMessage")); + } + + public static agentResponseMessageToJson(value: AgentResponseMessage): string { + return JSON.stringify(uncast(value, r("AgentResponseMessage")), null, 2); + } + + public static toAgentEventMessage(json: string): AgentEventMessage { + return cast(JSON.parse(json), r("AgentEventMessage")); + } + + public static agentEventMessageToJson(value: AgentEventMessage): string { + return JSON.stringify(uncast(value, r("AgentEventMessage")), null, 2); + } + + public static toAddContextListenerRequest(json: string): AddContextListenerRequest { + return cast(JSON.parse(json), r("AddContextListenerRequest")); + } + + public static addContextListenerRequestToJson(value: AddContextListenerRequest): string { + return JSON.stringify(uncast(value, r("AddContextListenerRequest")), null, 2); + } + + public static toAddContextListenerResponse(json: string): AddContextListenerResponse { + return cast(JSON.parse(json), r("AddContextListenerResponse")); + } + + public static addContextListenerResponseToJson(value: AddContextListenerResponse): string { + return JSON.stringify(uncast(value, r("AddContextListenerResponse")), null, 2); + } + + public static toAddIntentListenerRequest(json: string): AddIntentListenerRequest { + return cast(JSON.parse(json), r("AddIntentListenerRequest")); + } + + public static addIntentListenerRequestToJson(value: AddIntentListenerRequest): string { + return JSON.stringify(uncast(value, r("AddIntentListenerRequest")), null, 2); + } + + public static toAddIntentListenerResponse(json: string): AddIntentListenerResponse { + return cast(JSON.parse(json), r("AddIntentListenerResponse")); + } + + public static addIntentListenerResponseToJson(value: AddIntentListenerResponse): string { + return JSON.stringify(uncast(value, r("AddIntentListenerResponse")), null, 2); + } + + public static toBroadcastRequest(json: string): BroadcastRequest { + return cast(JSON.parse(json), r("BroadcastRequest")); + } + + public static broadcastRequestToJson(value: BroadcastRequest): string { + return JSON.stringify(uncast(value, r("BroadcastRequest")), null, 2); + } + + public static toBroadcastResponse(json: string): BroadcastResponse { + return cast(JSON.parse(json), r("BroadcastResponse")); + } + + public static broadcastResponseToJson(value: BroadcastResponse): string { + return JSON.stringify(uncast(value, r("BroadcastResponse")), null, 2); + } + + public static toChannelChangedEvent(json: string): ChannelChangedEvent { + return cast(JSON.parse(json), r("ChannelChangedEvent")); + } + + public static channelChangedEventToJson(value: ChannelChangedEvent): string { + return JSON.stringify(uncast(value, r("ChannelChangedEvent")), null, 2); + } + + public static toContextListenerUnsubscribeRequest(json: string): ContextListenerUnsubscribeRequest { + return cast(JSON.parse(json), r("ContextListenerUnsubscribeRequest")); + } + + public static contextListenerUnsubscribeRequestToJson(value: ContextListenerUnsubscribeRequest): string { + return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeRequest")), null, 2); + } + + public static toContextListenerUnsubscribeResponse(json: string): ContextListenerUnsubscribeResponse { + return cast(JSON.parse(json), r("ContextListenerUnsubscribeResponse")); + } + + public static contextListenerUnsubscribeResponseToJson(value: ContextListenerUnsubscribeResponse): string { + return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeResponse")), null, 2); + } + + public static toCreatePrivateChannelRequest(json: string): CreatePrivateChannelRequest { + return cast(JSON.parse(json), r("CreatePrivateChannelRequest")); + } + + public static createPrivateChannelRequestToJson(value: CreatePrivateChannelRequest): string { + return JSON.stringify(uncast(value, r("CreatePrivateChannelRequest")), null, 2); + } + + public static toCreatePrivateChannelResponse(json: string): CreatePrivateChannelResponse { + return cast(JSON.parse(json), r("CreatePrivateChannelResponse")); + } + + public static createPrivateChannelResponseToJson(value: CreatePrivateChannelResponse): string { + return JSON.stringify(uncast(value, r("CreatePrivateChannelResponse")), null, 2); + } + + public static toFindInstancesRequest(json: string): FindInstancesRequest { + return cast(JSON.parse(json), r("FindInstancesRequest")); + } + + public static findInstancesRequestToJson(value: FindInstancesRequest): string { + return JSON.stringify(uncast(value, r("FindInstancesRequest")), null, 2); + } + + public static toFindInstancesResponse(json: string): FindInstancesResponse { + return cast(JSON.parse(json), r("FindInstancesResponse")); + } + + public static findInstancesResponseToJson(value: FindInstancesResponse): string { + return JSON.stringify(uncast(value, r("FindInstancesResponse")), null, 2); + } + + public static toFindIntentRequest(json: string): FindIntentRequest { + return cast(JSON.parse(json), r("FindIntentRequest")); + } + + public static findIntentRequestToJson(value: FindIntentRequest): string { + return JSON.stringify(uncast(value, r("FindIntentRequest")), null, 2); + } + + public static toFindIntentResponse(json: string): FindIntentResponse { + return cast(JSON.parse(json), r("FindIntentResponse")); + } + + public static findIntentResponseToJson(value: FindIntentResponse): string { + return JSON.stringify(uncast(value, r("FindIntentResponse")), null, 2); + } + + public static toFindIntentsByContextRequest(json: string): FindIntentsByContextRequest { + return cast(JSON.parse(json), r("FindIntentsByContextRequest")); + } + + public static findIntentsByContextRequestToJson(value: FindIntentsByContextRequest): string { + return JSON.stringify(uncast(value, r("FindIntentsByContextRequest")), null, 2); + } + + public static toFindIntentsByContextsByContextResponse(json: string): FindIntentsByContextsByContextResponse { + return cast(JSON.parse(json), r("FindIntentsByContextsByContextResponse")); + } + + public static findIntentsByContextsByContextResponseToJson(value: FindIntentsByContextsByContextResponse): string { + return JSON.stringify(uncast(value, r("FindIntentsByContextsByContextResponse")), null, 2); + } + + public static toGetAppMetadataRequest(json: string): GetAppMetadataRequest { + return cast(JSON.parse(json), r("GetAppMetadataRequest")); + } + + public static getAppMetadataRequestToJson(value: GetAppMetadataRequest): string { + return JSON.stringify(uncast(value, r("GetAppMetadataRequest")), null, 2); + } + + public static toGetAppMetadataResponse(json: string): GetAppMetadataResponse { + return cast(JSON.parse(json), r("GetAppMetadataResponse")); + } + + public static getAppMetadataResponseToJson(value: GetAppMetadataResponse): string { + return JSON.stringify(uncast(value, r("GetAppMetadataResponse")), null, 2); + } + + public static toGetCurrentChannelRequest(json: string): GetCurrentChannelRequest { + return cast(JSON.parse(json), r("GetCurrentChannelRequest")); + } + + public static getCurrentChannelRequestToJson(value: GetCurrentChannelRequest): string { + return JSON.stringify(uncast(value, r("GetCurrentChannelRequest")), null, 2); + } + + public static toGetCurrentChannelResponse(json: string): GetCurrentChannelResponse { + return cast(JSON.parse(json), r("GetCurrentChannelResponse")); + } + + public static getCurrentChannelResponseToJson(value: GetCurrentChannelResponse): string { + return JSON.stringify(uncast(value, r("GetCurrentChannelResponse")), null, 2); + } + + public static toGetInfoRequest(json: string): GetInfoRequest { + return cast(JSON.parse(json), r("GetInfoRequest")); + } + + public static getInfoRequestToJson(value: GetInfoRequest): string { + return JSON.stringify(uncast(value, r("GetInfoRequest")), null, 2); + } + + public static toGetInfoResponse(json: string): GetInfoResponse { + return cast(JSON.parse(json), r("GetInfoResponse")); + } + + public static getInfoResponseToJson(value: GetInfoResponse): string { + return JSON.stringify(uncast(value, r("GetInfoResponse")), null, 2); + } + + public static toGetOrCreateChannelRequest(json: string): GetOrCreateChannelRequest { + return cast(JSON.parse(json), r("GetOrCreateChannelRequest")); + } + + public static getOrCreateChannelRequestToJson(value: GetOrCreateChannelRequest): string { + return JSON.stringify(uncast(value, r("GetOrCreateChannelRequest")), null, 2); + } + + public static toGetOrCreateChannelResponse(json: string): GetOrCreateChannelResponse { + return cast(JSON.parse(json), r("GetOrCreateChannelResponse")); + } + + public static getOrCreateChannelResponseToJson(value: GetOrCreateChannelResponse): string { + return JSON.stringify(uncast(value, r("GetOrCreateChannelResponse")), null, 2); + } + + public static toGetUserChannelsRequest(json: string): GetUserChannelsRequest { + return cast(JSON.parse(json), r("GetUserChannelsRequest")); + } + + public static getUserChannelsRequestToJson(value: GetUserChannelsRequest): string { + return JSON.stringify(uncast(value, r("GetUserChannelsRequest")), null, 2); + } + + public static toGetUserChannelsResponse(json: string): GetUserChannelsResponse { + return cast(JSON.parse(json), r("GetUserChannelsResponse")); + } + + public static getUserChannelsResponseToJson(value: GetUserChannelsResponse): string { + return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); + } + + public static toIntentListenerUnsubscribeRequest(json: string): IntentListenerUnsubscribeRequest { + return cast(JSON.parse(json), r("IntentListenerUnsubscribeRequest")); + } + + public static intentListenerUnsubscribeRequestToJson(value: IntentListenerUnsubscribeRequest): string { + return JSON.stringify(uncast(value, r("IntentListenerUnsubscribeRequest")), null, 2); + } + + public static toIntentListenerUnsubscribeResponse(json: string): IntentListenerUnsubscribeResponse { + return cast(JSON.parse(json), r("IntentListenerUnsubscribeResponse")); + } + + public static intentListenerUnsubscribeResponseToJson(value: IntentListenerUnsubscribeResponse): string { + return JSON.stringify(uncast(value, r("IntentListenerUnsubscribeResponse")), null, 2); + } + + public static toJoinUserChannelRequest(json: string): JoinUserChannelRequest { + return cast(JSON.parse(json), r("JoinUserChannelRequest")); + } + + public static joinUserChannelRequestToJson(value: JoinUserChannelRequest): string { + return JSON.stringify(uncast(value, r("JoinUserChannelRequest")), null, 2); + } + + public static toJoinUserChannelResponse(json: string): JoinUserChannelResponse { + return cast(JSON.parse(json), r("JoinUserChannelResponse")); + } + + public static joinUserChannelResponseToJson(value: JoinUserChannelResponse): string { + return JSON.stringify(uncast(value, r("JoinUserChannelResponse")), null, 2); + } + + public static toLeaveCurrentChannelRequest(json: string): LeaveCurrentChannelRequest { + return cast(JSON.parse(json), r("LeaveCurrentChannelRequest")); + } + + public static leaveCurrentChannelRequestToJson(value: LeaveCurrentChannelRequest): string { + return JSON.stringify(uncast(value, r("LeaveCurrentChannelRequest")), null, 2); + } + + public static toLeaveCurrentChannelResponse(json: string): LeaveCurrentChannelResponse { + return cast(JSON.parse(json), r("LeaveCurrentChannelResponse")); + } + + public static leaveCurrentChannelResponseToJson(value: LeaveCurrentChannelResponse): string { + return JSON.stringify(uncast(value, r("LeaveCurrentChannelResponse")), null, 2); + } + + public static toOpenRequest(json: string): OpenRequest { + return cast(JSON.parse(json), r("OpenRequest")); + } + + public static openRequestToJson(value: OpenRequest): string { + return JSON.stringify(uncast(value, r("OpenRequest")), null, 2); + } + + public static toOpenResponse(json: string): OpenResponse { + return cast(JSON.parse(json), r("OpenResponse")); + } + + public static openResponseToJson(value: OpenResponse): string { + return JSON.stringify(uncast(value, r("OpenResponse")), null, 2); + } + + public static toPrivateChannelAddEventListenerRequest(json: string): PrivateChannelAddEventListenerRequest { + return cast(JSON.parse(json), r("PrivateChannelAddEventListenerRequest")); + } + + public static privateChannelAddEventListenerRequestToJson(value: PrivateChannelAddEventListenerRequest): string { + return JSON.stringify(uncast(value, r("PrivateChannelAddEventListenerRequest")), null, 2); + } + + public static toPrivateChannelAddEventListenerResponse(json: string): PrivateChannelAddEventListenerResponse { + return cast(JSON.parse(json), r("PrivateChannelAddEventListenerResponse")); + } + + public static privateChannelAddEventListenerResponseToJson(value: PrivateChannelAddEventListenerResponse): string { + return JSON.stringify(uncast(value, r("PrivateChannelAddEventListenerResponse")), null, 2); + } + + public static toPrivateChannelDisconnectRequest(json: string): PrivateChannelDisconnectRequest { + return cast(JSON.parse(json), r("PrivateChannelDisconnectRequest")); + } + + public static privateChannelDisconnectRequestToJson(value: PrivateChannelDisconnectRequest): string { + return JSON.stringify(uncast(value, r("PrivateChannelDisconnectRequest")), null, 2); + } + + public static toPrivateChannelDisconnectResponse(json: string): PrivateChannelDisconnectResponse { + return cast(JSON.parse(json), r("PrivateChannelDisconnectResponse")); + } + + public static privateChannelDisconnectResponseToJson(value: PrivateChannelDisconnectResponse): string { + return JSON.stringify(uncast(value, r("PrivateChannelDisconnectResponse")), null, 2); + } + + public static toPrivateChannelOnAddContextListenerEvent(json: string): PrivateChannelOnAddContextListenerEvent { + return cast(JSON.parse(json), r("PrivateChannelOnAddContextListenerEvent")); + } + + public static privateChannelOnAddContextListenerEventToJson(value: PrivateChannelOnAddContextListenerEvent): string { + return JSON.stringify(uncast(value, r("PrivateChannelOnAddContextListenerEvent")), null, 2); + } + + public static toPrivateChannelOnDisconnectEvent(json: string): PrivateChannelOnDisconnectEvent { + return cast(JSON.parse(json), r("PrivateChannelOnDisconnectEvent")); + } + + public static privateChannelOnDisconnectEventToJson(value: PrivateChannelOnDisconnectEvent): string { + return JSON.stringify(uncast(value, r("PrivateChannelOnDisconnectEvent")), null, 2); + } + + public static toPrivateChannelOnUnsubscribeEventEvent(json: string): PrivateChannelOnUnsubscribeEventEvent { + return cast(JSON.parse(json), r("PrivateChannelOnUnsubscribeEventEvent")); + } + + public static privateChannelOnUnsubscribeEventEventToJson(value: PrivateChannelOnUnsubscribeEventEvent): string { + return JSON.stringify(uncast(value, r("PrivateChannelOnUnsubscribeEventEvent")), null, 2); + } + + public static toPrivateChannelUnsubscribeEventListenerRequest(json: string): PrivateChannelUnsubscribeEventListenerRequest { + return cast(JSON.parse(json), r("PrivateChannelUnsubscribeEventListenerRequest")); + } + + public static privateChannelUnsubscribeEventListenerRequestToJson(value: PrivateChannelUnsubscribeEventListenerRequest): string { + return JSON.stringify(uncast(value, r("PrivateChannelUnsubscribeEventListenerRequest")), null, 2); + } + + public static toPrivateChannelUnsubscribeEventListenerResponse(json: string): PrivateChannelUnsubscribeEventListenerResponse { + return cast(JSON.parse(json), r("PrivateChannelUnsubscribeEventListenerResponse")); + } + + public static privateChannelUnsubscribeEventListenerResponseToJson(value: PrivateChannelUnsubscribeEventListenerResponse): string { + return JSON.stringify(uncast(value, r("PrivateChannelUnsubscribeEventListenerResponse")), null, 2); + } + + public static toRaiseIntentForContextRequest(json: string): RaiseIntentForContextRequest { + return cast(JSON.parse(json), r("RaiseIntentForContextRequest")); + } + + public static raiseIntentForContextRequestToJson(value: RaiseIntentForContextRequest): string { + return JSON.stringify(uncast(value, r("RaiseIntentForContextRequest")), null, 2); + } + + public static toRaiseIntentForContextResponse(json: string): RaiseIntentForContextResponse { + return cast(JSON.parse(json), r("RaiseIntentForContextResponse")); + } + + public static raiseIntentForContextResponseToJson(value: RaiseIntentForContextResponse): string { + return JSON.stringify(uncast(value, r("RaiseIntentForContextResponse")), null, 2); + } + + public static toRaiseIntentRequest(json: string): RaiseIntentRequest { + return cast(JSON.parse(json), r("RaiseIntentRequest")); + } + + public static raiseIntentRequestToJson(value: RaiseIntentRequest): string { + return JSON.stringify(uncast(value, r("RaiseIntentRequest")), null, 2); + } + + public static toRaiseIntentResponse(json: string): RaiseIntentResponse { + return cast(JSON.parse(json), r("RaiseIntentResponse")); + } + + public static raiseIntentResponseToJson(value: RaiseIntentResponse): string { + return JSON.stringify(uncast(value, r("RaiseIntentResponse")), null, 2); + } + + public static toRaiseIntentResultResponse(json: string): RaiseIntentResultResponse { + return cast(JSON.parse(json), r("RaiseIntentResultResponse")); + } + + public static raiseIntentResultResponseToJson(value: RaiseIntentResultResponse): string { + return JSON.stringify(uncast(value, r("RaiseIntentResultResponse")), null, 2); + } + + public static toWebConnectionProtocolHello(json: string): WebConnectionProtocolHello { + return cast(JSON.parse(json), r("WebConnectionProtocolHello")); + } + + public static webConnectionProtocolHelloToJson(value: WebConnectionProtocolHello): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocolHello")), null, 2); + } + + public static toWebConnectionProtocolLoadURL(json: string): WebConnectionProtocolLoadURL { + return cast(JSON.parse(json), r("WebConnectionProtocolLoadURL")); + } + + public static webConnectionProtocolLoadURLToJson(value: WebConnectionProtocolLoadURL): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocolLoadURL")), null, 2); + } + + public static toWCP3Handshake(json: string): WCP3Handshake { + return cast(JSON.parse(json), r("WCP3Handshake")); + } + + public static wCP3HandshakeToJson(value: WCP3Handshake): string { + return JSON.stringify(uncast(value, r("WCP3Handshake")), null, 2); + } + + public static toWCP4ValidateAppIdentity(json: string): WCP4ValidateAppIdentity { + return cast(JSON.parse(json), r("WCP4ValidateAppIdentity")); + } + + public static wCP4ValidateAppIdentityToJson(value: WCP4ValidateAppIdentity): string { + return JSON.stringify(uncast(value, r("WCP4ValidateAppIdentity")), null, 2); + } + + public static toWCP5ValidateAppIdentityFailedResponse(json: string): WCP5ValidateAppIdentityFailedResponse { + return cast(JSON.parse(json), r("WCP5ValidateAppIdentityFailedResponse")); + } + + public static wCP5ValidateAppIdentityFailedResponseToJson(value: WCP5ValidateAppIdentityFailedResponse): string { + return JSON.stringify(uncast(value, r("WCP5ValidateAppIdentityFailedResponse")), null, 2); + } + + public static toWebConnectionProtocolMessage(json: string): WebConnectionProtocolMessage { + return cast(JSON.parse(json), r("WebConnectionProtocolMessage")); + } + + public static webConnectionProtocolMessageToJson(value: WebConnectionProtocolMessage): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocolMessage")), null, 2); + } +} + +function invalidValue(typ: any, val: any, key: any, parent: any = ''): never { + const prettyTyp = prettyTypeName(typ); + const parentText = parent ? ` on ${parent}` : ''; + const keyText = key ? ` for key "${key}"` : ''; + throw Error(`Invalid value${keyText}${parentText}. Expected ${prettyTyp} but got ${JSON.stringify(val)}`); +} + +function prettyTypeName(typ: any): string { + if (Array.isArray(typ)) { + if (typ.length === 2 && typ[0] === undefined) { + return `an optional ${prettyTypeName(typ[1])}`; + } else { + return `one of [${typ.map(a => { return prettyTypeName(a); }).join(", ")}]`; + } + } else if (typeof typ === "object" && typ.literal !== undefined) { + return typ.literal; + } else { + return typeof typ; + } +} + +function jsonToJSProps(typ: any): any { + if (typ.jsonToJS === undefined) { + const map: any = {}; + typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ }); + typ.jsonToJS = map; + } + return typ.jsonToJS; +} + +function jsToJSONProps(typ: any): any { + if (typ.jsToJSON === undefined) { + const map: any = {}; + typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ }); + typ.jsToJSON = map; + } + return typ.jsToJSON; +} + +function transform(val: any, typ: any, getProps: any, key: any = '', parent: any = ''): any { + function transformPrimitive(typ: string, val: any): any { + if (typeof typ === typeof val) return val; + return invalidValue(typ, val, key, parent); + } + + function transformUnion(typs: any[], val: any): any { + // val must validate against one typ in typs + const l = typs.length; + for (let i = 0; i < l; i++) { + const typ = typs[i]; + try { + return transform(val, typ, getProps); + } catch (_) {} + } + return invalidValue(typs, val, key, parent); + } + + function transformEnum(cases: string[], val: any): any { + if (cases.indexOf(val) !== -1) return val; + return invalidValue(cases.map(a => { return l(a); }), val, key, parent); + } + + function transformArray(typ: any, val: any): any { + // val must be an array with no invalid elements + if (!Array.isArray(val)) return invalidValue(l("array"), val, key, parent); + return val.map(el => transform(el, typ, getProps)); + } + + function transformDate(val: any): any { + if (val === null) { + return null; + } + const d = new Date(val); + if (isNaN(d.valueOf())) { + return invalidValue(l("Date"), val, key, parent); + } + return d; + } + + function transformObject(props: { [k: string]: any }, additional: any, val: any): any { + if (val === null || typeof val !== "object" || Array.isArray(val)) { + return invalidValue(l(ref || "object"), val, key, parent); + } + const result: any = {}; + Object.getOwnPropertyNames(props).forEach(key => { + const prop = props[key]; + const v = Object.prototype.hasOwnProperty.call(val, key) ? val[key] : undefined; + result[prop.key] = transform(v, prop.typ, getProps, key, ref); + }); + Object.getOwnPropertyNames(val).forEach(key => { + if (!Object.prototype.hasOwnProperty.call(props, key)) { + result[key] = transform(val[key], additional, getProps, key, ref); + } + }); + return result; + } + + if (typ === "any") return val; + if (typ === null) { + if (val === null) return val; + return invalidValue(typ, val, key, parent); + } + if (typ === false) return invalidValue(typ, val, key, parent); + let ref: any = undefined; + while (typeof typ === "object" && typ.ref !== undefined) { + ref = typ.ref; + typ = typeMap[typ.ref]; + } + if (Array.isArray(typ)) return transformEnum(typ, val); + if (typeof typ === "object") { + return typ.hasOwnProperty("unionMembers") ? transformUnion(typ.unionMembers, val) + : typ.hasOwnProperty("arrayItems") ? transformArray(typ.arrayItems, val) + : typ.hasOwnProperty("props") ? transformObject(getProps(typ), typ.additional, val) + : invalidValue(typ, val, key, parent); + } + // Numbers can be parsed by Date but shouldn't be. + if (typ === Date && typeof val !== "number") return transformDate(val); + return transformPrimitive(typ, val); +} + +function cast(val: any, typ: any): T { + return transform(val, typ, jsonToJSProps); +} + +function uncast(val: T, typ: any): any { + return transform(val, typ, jsToJSONProps); +} + +function l(typ: any) { + return { literal: typ }; +} + +function a(typ: any) { + return { arrayItems: typ }; +} + +function u(...typs: any[]) { + return { unionMembers: typs }; +} + +function o(props: any[], additional: any) { + return { props, additional }; +} + +function m(additional: any) { + return { props: [], additional }; +} + +function r(name: string) { + return { ref: name }; +} + +const typeMap: any = { + "AppRequestMessage": o([ + { json: "meta", js: "meta", typ: r("AppRequestMessageMeta") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("RequestMessageType") }, + ], false), + "AppRequestMessageMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AppIdentifier": o([ + { json: "appId", js: "appId", typ: "" }, + { json: "desktopAgent", js: "desktopAgent", typ: u(undefined, "") }, + { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, + ], "any"), + "AgentResponseMessage": o([ + { json: "meta", js: "meta", typ: r("AgentResponseMessageMeta") }, + { json: "payload", js: "payload", typ: r("AgentResponseMessageResponsePayload") }, + { json: "type", js: "type", typ: r("ResponseMessageType") }, + ], false), + "AgentResponseMessageMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "responseUuid", js: "responseUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AgentResponseMessageResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + ], "any"), + "AgentEventMessage": o([ + { json: "meta", js: "meta", typ: r("AgentEventMessageMeta") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("EventMessageType") }, + ], false), + "AgentEventMessageMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AddContextListenerRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("AddContextListenerRequestPayload") }, + { json: "type", js: "type", typ: r("AddContextListenerRequestType") }, + ], false), + "AddContextListenerRequestMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AddContextListenerRequestPayload": o([ + { json: "channelId", js: "channelId", typ: u(null, "") }, + { json: "contextType", js: "contextType", typ: u(null, "") }, + ], false), + "AddContextListenerResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("AddContextListenerResponsePayload") }, + { json: "type", js: "type", typ: r("AddContextListenerResponseType") }, + ], false), + "AddContextListenerResponseMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "responseUuid", js: "responseUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AddContextListenerResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, + ], false), + "AddIntentListenerRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("AddIntentListenerRequestPayload") }, + { json: "type", js: "type", typ: r("AddIntentListenerRequestType") }, + ], false), + "AddIntentListenerRequestPayload": o([ + { json: "intent", js: "intent", typ: "" }, + ], false), + "AddIntentListenerResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("AddIntentListenerResponsePayload") }, + { json: "type", js: "type", typ: r("AddIntentListenerResponseType") }, + ], false), + "AddIntentListenerResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FluffyError")) }, + { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, + ], "any"), + "BroadcastRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastRequestPayload") }, + { json: "type", js: "type", typ: r("BroadcastRequestType") }, + ], false), + "BroadcastRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + { json: "context", js: "context", typ: r("Context") }, + ], false), + "Context": o([ + { json: "id", js: "id", typ: u(undefined, m("any")) }, + { json: "name", js: "name", typ: u(undefined, "") }, + { json: "type", js: "type", typ: "" }, + ], "any"), + "BroadcastResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("BroadcastResponseType") }, + ], false), + "BroadcastResponseResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + ], "any"), + "ChannelChangedEvent": o([ + { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "payload", js: "payload", typ: r("ChannelChangedEventPayload") }, + { json: "type", js: "type", typ: r("ChannelChangedEventType") }, + ], false), + "ChannelChangedEventMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "ChannelChangedEventPayload": o([ + { json: "newChannelId", js: "newChannelId", typ: u(null, "") }, + ], false), + "ContextListenerUnsubscribeRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("ContextListenerUnsubscribeRequestPayload") }, + { json: "type", js: "type", typ: r("ContextListenerUnsubscribeRequestType") }, + ], false), + "ContextListenerUnsubscribeRequestPayload": o([ + { json: "listenerUUID", js: "listenerUUID", typ: "" }, + ], false), + "ContextListenerUnsubscribeResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("ContextListenerUnsubscribeResponseType") }, + ], false), + "CreatePrivateChannelRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("CreatePrivateChannelRequestPayload") }, + { json: "type", js: "type", typ: r("CreatePrivateChannelRequestType") }, + ], false), + "CreatePrivateChannelRequestPayload": o([ + ], false), + "CreatePrivateChannelResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("CreatePrivateChannelResponsePayload") }, + { json: "type", js: "type", typ: r("CreatePrivateChannelResponseType") }, + ], false), + "CreatePrivateChannelResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "privateChannel", js: "privateChannel", typ: u(undefined, r("Channel")) }, + ], false), + "Channel": o([ + { json: "displayMetadata", js: "displayMetadata", typ: u(undefined, r("DisplayMetadata")) }, + { json: "id", js: "id", typ: "" }, + { json: "type", js: "type", typ: r("Type") }, + ], false), + "DisplayMetadata": o([ + { json: "color", js: "color", typ: u(undefined, "") }, + { json: "glyph", js: "glyph", typ: u(undefined, "") }, + { json: "name", js: "name", typ: u(undefined, "") }, + ], false), + "FindInstancesRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("FindInstancesRequestPayload") }, + { json: "type", js: "type", typ: r("FindInstancesRequestType") }, + ], false), + "FindInstancesRequestPayload": o([ + { json: "app", js: "app", typ: r("AppIdentifier") }, + ], false), + "FindInstancesResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("FindInstancesResponsePayload") }, + { json: "type", js: "type", typ: r("FindInstancesResponseType") }, + ], false), + "FindInstancesResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "appIdentifiers", js: "appIdentifiers", typ: u(undefined, a(r("AppMetadata"))) }, + ], false), + "AppMetadata": o([ + { json: "appId", js: "appId", typ: "" }, + { json: "description", js: "description", typ: u(undefined, "") }, + { json: "desktopAgent", js: "desktopAgent", typ: u(undefined, "") }, + { json: "icons", js: "icons", typ: u(undefined, a(r("Icon"))) }, + { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, + { json: "instanceMetadata", js: "instanceMetadata", typ: u(undefined, m("any")) }, + { json: "name", js: "name", typ: u(undefined, "") }, + { json: "resultType", js: "resultType", typ: u(undefined, u(null, "")) }, + { json: "screenshots", js: "screenshots", typ: u(undefined, a(r("Image"))) }, + { json: "title", js: "title", typ: u(undefined, "") }, + { json: "tooltip", js: "tooltip", typ: u(undefined, "") }, + { json: "version", js: "version", typ: u(undefined, "") }, + ], false), + "Icon": o([ + { json: "size", js: "size", typ: u(undefined, "") }, + { json: "src", js: "src", typ: "" }, + { json: "type", js: "type", typ: u(undefined, "") }, + ], false), + "Image": o([ + { json: "label", js: "label", typ: u(undefined, "") }, + { json: "size", js: "size", typ: u(undefined, "") }, + { json: "src", js: "src", typ: "" }, + { json: "type", js: "type", typ: u(undefined, "") }, + ], false), + "FindIntentRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("FindIntentRequestPayload") }, + { json: "type", js: "type", typ: r("FindIntentRequestType") }, + ], false), + "FindIntentRequestPayload": o([ + { json: "context", js: "context", typ: u(undefined, r("Context")) }, + { json: "intent", js: "intent", typ: "" }, + { json: "resultType", js: "resultType", typ: u(undefined, "") }, + ], false), + "FindIntentResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("FindIntentResponsePayload") }, + { json: "type", js: "type", typ: r("FindIntentResponseType") }, + ], false), + "FindIntentResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "appIntent", js: "appIntent", typ: u(undefined, r("AppIntent")) }, + ], false), + "AppIntent": o([ + { json: "apps", js: "apps", typ: a(r("AppMetadata")) }, + { json: "intent", js: "intent", typ: r("IntentMetadata") }, + ], false), + "IntentMetadata": o([ + { json: "displayName", js: "displayName", typ: "" }, + { json: "name", js: "name", typ: "" }, + ], false), + "FindIntentsByContextRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("FindIntentsByContextRequestPayload") }, + { json: "type", js: "type", typ: r("FindIntentsByContextRequestType") }, + ], false), + "FindIntentsByContextRequestPayload": o([ + { json: "context", js: "context", typ: r("Context") }, + ], false), + "FindIntentsByContextsByContextResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("FindIntentsByContextsByContextResponsePayload") }, + { json: "type", js: "type", typ: r("FindIntentsByContextsByContextResponseType") }, + ], false), + "FindIntentsByContextsByContextResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "appIntents", js: "appIntents", typ: u(undefined, a(r("AppIntent"))) }, + ], false), + "GetAppMetadataRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetAppMetadataRequestPayload") }, + { json: "type", js: "type", typ: r("GetAppMetadataRequestType") }, + ], false), + "GetAppMetadataRequestPayload": o([ + { json: "app", js: "app", typ: r("AppIdentifier") }, + ], false), + "GetAppMetadataResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetAppMetadataResponsePayload") }, + { json: "type", js: "type", typ: r("GetAppMetadataResponseType") }, + ], false), + "GetAppMetadataResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "appMetadata", js: "appMetadata", typ: u(undefined, r("AppMetadata")) }, + ], false), + "GetCurrentChannelRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetCurrentChannelRequestPayload") }, + { json: "type", js: "type", typ: r("GetCurrentChannelRequestType") }, + ], false), + "GetCurrentChannelRequestPayload": o([ + ], false), + "GetCurrentChannelResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetCurrentChannelResponsePayload") }, + { json: "type", js: "type", typ: r("GetCurrentChannelResponseType") }, + ], false), + "GetCurrentChannelResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + { json: "channel", js: "channel", typ: u(undefined, u(r("Channel"), null)) }, + ], false), + "GetInfoRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetInfoRequestPayload") }, + { json: "type", js: "type", typ: r("GetInfoRequestType") }, + ], false), + "GetInfoRequestPayload": o([ + ], false), + "GetInfoResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetInfoResponsePayload") }, + { json: "type", js: "type", typ: r("GetInfoResponseType") }, + ], false), + "GetInfoResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + { json: "implementationMetadata", js: "implementationMetadata", typ: u(undefined, r("ImplementationMetadata")) }, + ], false), + "ImplementationMetadata": o([ + { json: "appMetadata", js: "appMetadata", typ: r("AppMetadata") }, + { json: "fdc3Version", js: "fdc3Version", typ: "" }, + { json: "optionalFeatures", js: "optionalFeatures", typ: r("OptionalFeatures") }, + { json: "provider", js: "provider", typ: "" }, + { json: "providerVersion", js: "providerVersion", typ: u(undefined, "") }, + ], false), + "OptionalFeatures": o([ + { json: "DesktopAgentBridging", js: "DesktopAgentBridging", typ: true }, + { json: "OriginatingAppMetadata", js: "OriginatingAppMetadata", typ: true }, + { json: "UserChannelMembershipAPIs", js: "UserChannelMembershipAPIs", typ: true }, + ], false), + "GetOrCreateChannelRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetOrCreateChannelRequestPayload") }, + { json: "type", js: "type", typ: r("GetOrCreateChannelRequestType") }, + ], false), + "GetOrCreateChannelRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + ], false), + "GetOrCreateChannelResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetOrCreateChannelResponsePayload") }, + { json: "type", js: "type", typ: r("GetOrCreateChannelResponseType") }, + ], false), + "GetOrCreateChannelResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, + ], false), + "GetUserChannelsRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetUserChannelsRequestPayload") }, + { json: "type", js: "type", typ: r("GetUserChannelsRequestType") }, + ], false), + "GetUserChannelsRequestPayload": o([ + ], false), + "GetUserChannelsResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetUserChannelsResponsePayload") }, + { json: "type", js: "type", typ: r("GetUserChannelsResponseType") }, + ], false), + "GetUserChannelsResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, + ], false), + "IntentListenerUnsubscribeRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("IntentListenerUnsubscribeRequestPayload") }, + { json: "type", js: "type", typ: r("IntentListenerUnsubscribeRequestType") }, + ], false), + "IntentListenerUnsubscribeRequestPayload": o([ + { json: "listenerUUID", js: "listenerUUID", typ: "" }, + ], false), + "IntentListenerUnsubscribeResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("IntentListenerUnsubscribeResponseType") }, + ], false), + "JoinUserChannelRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("JoinUserChannelRequestPayload") }, + { json: "type", js: "type", typ: r("JoinUserChannelRequestType") }, + ], false), + "JoinUserChannelRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + ], false), + "JoinUserChannelResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("JoinUserChannelResponsePayload") }, + { json: "type", js: "type", typ: r("JoinUserChannelResponseType") }, + ], false), + "JoinUserChannelResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + ], false), + "LeaveCurrentChannelRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("LeaveCurrentChannelRequestPayload") }, + { json: "type", js: "type", typ: r("LeaveCurrentChannelRequestType") }, + ], false), + "LeaveCurrentChannelRequestPayload": o([ + ], false), + "LeaveCurrentChannelResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("LeaveCurrentChannelResponsePayload") }, + { json: "type", js: "type", typ: r("LeaveCurrentChannelResponseType") }, + ], false), + "LeaveCurrentChannelResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + ], false), + "OpenRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("OpenRequestPayload") }, + { json: "type", js: "type", typ: r("OpenRequestType") }, + ], false), + "OpenRequestPayload": o([ + { json: "app", js: "app", typ: r("AppIdentifier") }, + ], false), + "OpenResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("OpenResponsePayload") }, + { json: "type", js: "type", typ: r("OpenResponseType") }, + ], false), + "OpenResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("OpenErrorResponsePayload")) }, + { json: "appIdentifier", js: "appIdentifier", typ: u(undefined, r("AppIdentifier")) }, + ], false), + "PrivateChannelAddEventListenerRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("TPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelAddEventListenerRequestType") }, + ], false), + "TPayload": o([ + { json: "contextType", js: "contextType", typ: u(null, "") }, + { json: "privateChannelId", js: "privateChannelId", typ: "" }, + ], false), + "PrivateChannelAddEventListenerResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelAddEventListenerResponsePayload") }, + { json: "type", js: "type", typ: r("PrivateChannelAddEventListenerResponseType") }, + ], false), + "PrivateChannelAddEventListenerResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, + ], "any"), + "PrivateChannelDisconnectRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelDisconnectRequestPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelDisconnectRequestType") }, + ], false), + "PrivateChannelDisconnectRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + ], false), + "PrivateChannelDisconnectResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelDisconnectResponsePayload") }, + { json: "type", js: "type", typ: r("PrivateChannelDisconnectResponseType") }, + ], false), + "PrivateChannelDisconnectResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + ], false), + "PrivateChannelOnAddContextListenerEvent": o([ + { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelOnAddContextListenerEventPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelOnAddContextListenerEventType") }, + ], false), + "PrivateChannelOnAddContextListenerEventPayload": o([ + { json: "contextType", js: "contextType", typ: u(null, "") }, + { json: "privateChannelId", js: "privateChannelId", typ: "" }, + ], false), + "PrivateChannelOnDisconnectEvent": o([ + { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelOnDisconnectEventPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelOnDisconnectEventType") }, + ], false), + "PrivateChannelOnDisconnectEventPayload": o([ + { json: "privateChannelId", js: "privateChannelId", typ: "" }, + ], false), + "PrivateChannelOnUnsubscribeEventEvent": o([ + { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventEventPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventEventType") }, + ], false), + "PrivateChannelOnUnsubscribeEventEventPayload": o([ + { json: "contextType", js: "contextType", typ: u(null, "") }, + { json: "privateChannelId", js: "privateChannelId", typ: "" }, + ], false), + "PrivateChannelUnsubscribeEventListenerRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("PrivateChannelUnsubscribeEventListenerRequestPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelUnsubscribeEventListenerRequestType") }, + ], false), + "PrivateChannelUnsubscribeEventListenerRequestPayload": o([ + { json: "listenerUUID", js: "listenerUUID", typ: "" }, + ], false), + "PrivateChannelUnsubscribeEventListenerResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("PrivateChannelUnsubscribeEventListenerResponseType") }, + ], false), + "RaiseIntentForContextRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("RaiseIntentForContextRequestPayload") }, + { json: "type", js: "type", typ: r("RaiseIntentForContextRequestType") }, + ], false), + "RaiseIntentForContextRequestPayload": o([ + { json: "app", js: "app", typ: u(undefined, r("AppIdentifier")) }, + { json: "context", js: "context", typ: r("Context") }, + ], false), + "RaiseIntentForContextResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("RaiseIntentForContextResponsePayload") }, + { json: "type", js: "type", typ: r("RaiseIntentForContextResponseType") }, + ], false), + "RaiseIntentForContextResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "intentResolution", js: "intentResolution", typ: u(undefined, r("IntentResolution")) }, + { json: "appIntents", js: "appIntents", typ: u(undefined, a(r("AppIntent"))) }, + ], false), + "IntentResolution": o([ + { json: "intent", js: "intent", typ: "" }, + { json: "source", js: "source", typ: r("AppIdentifier") }, + ], false), + "RaiseIntentRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("RaiseIntentRequestPayload") }, + { json: "type", js: "type", typ: r("RaiseIntentRequestType") }, + ], false), + "RaiseIntentRequestPayload": o([ + { json: "app", js: "app", typ: u(undefined, r("AppIdentifier")) }, + { json: "context", js: "context", typ: r("Context") }, + { json: "intent", js: "intent", typ: "" }, + ], false), + "RaiseIntentResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("RaiseIntentResponsePayload") }, + { json: "type", js: "type", typ: r("RaiseIntentResponseType") }, + ], false), + "RaiseIntentResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "intentResolution", js: "intentResolution", typ: u(undefined, r("IntentResolution")) }, + { json: "appIntent", js: "appIntent", typ: u(undefined, r("AppIntent")) }, + ], false), + "RaiseIntentResultResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("ResponsePayload") }, + { json: "type", js: "type", typ: r("RaiseIntentResultResponseType") }, + ], false), + "ResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + { json: "intentResult", js: "intentResult", typ: u(undefined, r("IntentResult")) }, + ], false), + "IntentResult": o([ + { json: "context", js: "context", typ: u(undefined, r("Context")) }, + { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, + ], false), + "WebConnectionProtocolHello": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocolHelloPayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocolHelloType") }, + ], false), + "ConnectionStepMetadata": o([ + { json: "connectionAttemptUuid", js: "connectionAttemptUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "WebConnectionProtocolHelloPayload": o([ + { json: "channelSelector", js: "channelSelector", typ: u(undefined, true) }, + { json: "fdc3Version", js: "fdc3Version", typ: "" }, + { json: "resolver", js: "resolver", typ: u(undefined, true) }, + ], "any"), + "WebConnectionProtocolLoadURL": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocolLoadURLPayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocolLoadURLType") }, + ], false), + "WebConnectionProtocolLoadURLPayload": o([ + { json: "iframeUrl", js: "iframeUrl", typ: "" }, + ], "any"), + "WCP3Handshake": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("WCP3HandshakePayload") }, + { json: "type", js: "type", typ: r("WCP3HandshakeType") }, + ], false), + "WCP3HandshakePayload": o([ + { json: "channelSelector", js: "channelSelector", typ: u(true, "") }, + { json: "fdc3Version", js: "fdc3Version", typ: "" }, + { json: "resolver", js: "resolver", typ: u(true, "") }, + ], false), + "WCP4ValidateAppIdentity": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("PayloadClass") }, + { json: "type", js: "type", typ: r("WCP4ValidateAppIdentityType") }, + ], false), + "PayloadClass": o([ + { json: "appDUrl", js: "appDUrl", typ: u(undefined, "") }, + { json: "appId", js: "appId", typ: "" }, + { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, + { json: "instanceUuid", js: "instanceUuid", typ: u(undefined, "") }, + ], false), + "WCP5ValidateAppIdentityFailedResponse": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("WCP5ValidateAppIdentityFailedResponsePayload") }, + { json: "type", js: "type", typ: r("WCP5ValidateAppIdentityFailedResponseType") }, + ], false), + "WCP5ValidateAppIdentityFailedResponsePayload": o([ + { json: "appId", js: "appId", typ: "" }, + { json: "implementationMetadata", js: "implementationMetadata", typ: r("ImplementationMetadata") }, + { json: "instanceId", js: "instanceId", typ: "" }, + { json: "instanceUuid", js: "instanceUuid", typ: "" }, + ], false), + "WebConnectionProtocolMessage": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("ConnectionStepMessageType") }, + ], false), + "RequestMessageType": [ + "addContextListenerRequest", + "addIntentListenerRequest", + "broadcastRequest", + "contextListenerUnsubscribeRequest", + "createPrivateChannelRequest", + "findInstancesRequest", + "findIntentRequest", + "findIntentsByContextRequest", + "getAppMetadataRequest", + "getCurrentChannelRequest", + "getInfoRequest", + "getOrCreateChannelRequest", + "getUserChannelsRequest", + "intentListenerUnsubscribeRequest", + "joinUserChannelRequest", + "leaveCurrentChannelRequest", + "openRequest", + "privateChannelAddEventListenerRequest", + "privateChannelDisconnectRequest", + "privateChannelUnsubscribeEventListenerRequest", + "raiseIntentForContextRequest", + "raiseIntentRequest", + ], + "ResponsePayloadError": [ + "AccessDenied", + "AgentDisconnected", + "AppNotFound", + "AppTimeout", + "CreationFailed", + "DesktopAgentNotFound", + "ErrorOnLaunch", + "IntentDeliveryFailed", + "IntentHandlerRejected", + "MalformedContext", + "MalformedMessage", + "NoAppsFound", + "NoChannelFound", + "NoResultReturned", + "NotConnectedToBridge", + "ResolverTimeout", + "ResolverUnavailable", + "ResponseToBridgeTimedOut", + "TargetAppUnavailable", + "TargetInstanceUnavailable", + "UserCancelledResolution", + ], + "ResponseMessageType": [ + "addContextListenerResponse", + "addIntentListenerResponse", + "broadcastResponse", + "contextListenerUnsubscribeResponse", + "createPrivateChannelResponse", + "findInstancesResponse", + "findIntentResponse", + "findIntentsByContextResponse", + "getAppMetadataResponse", + "getCurrentChannelResponse", + "getInfoResponse", + "getOrCreateChannelResponse", + "getUserChannelsResponse", + "intentListenerUnsubscribeResponse", + "joinUserChannelResponse", + "leaveCurrentChannelResponse", + "openResponse", + "privateChannelAddEventListenerResponse", + "privateChannelDisconnectResponse", + "privateChannelUnsubscribeEventListenerResponse", + "raiseIntentForContextResponse", + "raiseIntentResponse", + "raiseIntentResultResponse", + ], + "EventMessageType": [ + "channelChangedEvent", + "privateChannelOnAddContextListenerEvent", + "privateChannelOnDisconnectEvent", + "privateChannelOnUnsubscribeEvent", + ], + "AddContextListenerRequestType": [ + "addContextListenerRequest", + ], + "PurpleError": [ + "AccessDenied", + "CreationFailed", + "MalformedContext", + "NoChannelFound", + ], + "AddContextListenerResponseType": [ + "addContextListenerResponse", + ], + "AddIntentListenerRequestType": [ + "addIntentListenerRequest", + ], + "FluffyError": [ + "DesktopAgentNotFound", + "IntentDeliveryFailed", + "MalformedContext", + "NoAppsFound", + "ResolverTimeout", + "ResolverUnavailable", + "TargetAppUnavailable", + "TargetInstanceUnavailable", + "UserCancelledResolution", + ], + "AddIntentListenerResponseType": [ + "addIntentListenerResponse", + ], + "BroadcastRequestType": [ + "broadcastRequest", + ], + "BroadcastResponseType": [ + "broadcastResponse", + ], + "ChannelChangedEventType": [ + "channelChangedEvent", + ], + "ContextListenerUnsubscribeRequestType": [ + "contextListenerUnsubscribeRequest", + ], + "ContextListenerUnsubscribeResponseType": [ + "contextListenerUnsubscribeResponse", + ], + "CreatePrivateChannelRequestType": [ + "createPrivateChannelRequest", + ], + "Type": [ + "app", + "private", + "user", + ], + "CreatePrivateChannelResponseType": [ + "createPrivateChannelResponse", + ], + "FindInstancesRequestType": [ + "findInstancesRequest", + ], + "FindInstancesErrors": [ + "AgentDisconnected", + "DesktopAgentNotFound", + "IntentDeliveryFailed", + "MalformedContext", + "MalformedMessage", + "NoAppsFound", + "NotConnectedToBridge", + "ResolverTimeout", + "ResolverUnavailable", + "ResponseToBridgeTimedOut", + "TargetAppUnavailable", + "TargetInstanceUnavailable", + "UserCancelledResolution", + ], + "FindInstancesResponseType": [ + "findInstancesResponse", + ], + "FindIntentRequestType": [ + "findIntentRequest", + ], + "FindIntentResponseType": [ + "findIntentResponse", + ], + "FindIntentsByContextRequestType": [ + "findIntentsByContextRequest", + ], + "FindIntentsByContextsByContextResponseType": [ + "findIntentsByContextResponse", + ], + "GetAppMetadataRequestType": [ + "getAppMetadataRequest", + ], + "GetAppMetadataResponseType": [ + "getAppMetadataResponse", + ], + "GetCurrentChannelRequestType": [ + "getCurrentChannelRequest", + ], + "GetCurrentChannelResponseType": [ + "getCurrentChannelResponse", + ], + "GetInfoRequestType": [ + "getInfoRequest", + ], + "GetInfoResponseType": [ + "getInfoResponse", + ], + "GetOrCreateChannelRequestType": [ + "getOrCreateChannelRequest", + ], + "GetOrCreateChannelResponseType": [ + "getOrCreateChannelResponse", + ], + "GetUserChannelsRequestType": [ + "getUserChannelsRequest", + ], + "GetUserChannelsResponseType": [ + "getUserChannelsResponse", + ], + "IntentListenerUnsubscribeRequestType": [ + "intentListenerUnsubscribeRequest", + ], + "IntentListenerUnsubscribeResponseType": [ + "intentListenerUnsubscribeResponse", + ], + "JoinUserChannelRequestType": [ + "joinUserChannelRequest", + ], + "JoinUserChannelResponseType": [ + "joinUserChannelResponse", + ], + "LeaveCurrentChannelRequestType": [ + "leaveCurrentChannelRequest", + ], + "LeaveCurrentChannelResponseType": [ + "leaveCurrentChannelResponse", + ], + "OpenRequestType": [ + "openRequest", + ], + "OpenErrorResponsePayload": [ + "AgentDisconnected", + "AppNotFound", + "AppTimeout", + "DesktopAgentNotFound", + "ErrorOnLaunch", + "MalformedContext", + "MalformedMessage", + "NotConnectedToBridge", + "ResolverUnavailable", + "ResponseToBridgeTimedOut", + ], + "OpenResponseType": [ + "openResponse", + ], + "PrivateChannelAddEventListenerRequestType": [ + "privateChannelAddEventListenerRequest", + ], + "PrivateChannelAddEventListenerResponseType": [ + "privateChannelAddEventListenerResponse", + ], + "PrivateChannelDisconnectRequestType": [ + "privateChannelDisconnectRequest", + ], + "PrivateChannelDisconnectResponseType": [ + "privateChannelDisconnectResponse", + ], + "PrivateChannelOnAddContextListenerEventType": [ + "privateChannelOnAddContextListenerEvent", + ], + "PrivateChannelOnDisconnectEventType": [ + "privateChannelOnDisconnectEvent", + ], + "PrivateChannelOnUnsubscribeEventEventType": [ + "privateChannelOnUnsubscribeEvent", + ], + "PrivateChannelUnsubscribeEventListenerRequestType": [ + "privateChannelUnsubscribeEventListenerRequest", + ], + "PrivateChannelUnsubscribeEventListenerResponseType": [ + "privateChannelUnsubscribeEventListenerResponse", + ], + "RaiseIntentForContextRequestType": [ + "raiseIntentForContextRequest", + ], + "RaiseIntentForContextResponseType": [ + "raiseIntentForContextResponse", + ], + "RaiseIntentRequestType": [ + "raiseIntentRequest", + ], + "RaiseIntentResponseType": [ + "raiseIntentResponse", + ], + "RaiseIntentResultResponseType": [ + "raiseIntentResultResponse", + ], + "WebConnectionProtocolHelloType": [ + "WCP1Hello", + ], + "WebConnectionProtocolLoadURLType": [ + "WCP2LoadUrl", + ], + "WCP3HandshakeType": [ + "WCP3Handshake", + ], + "WCP4ValidateAppIdentityType": [ + "WCP4ValidateAppIdentity", + ], + "WCP5ValidateAppIdentityFailedResponseType": [ + "WCP5ValidateAppIdentityResponse", + ], + "ConnectionStepMessageType": [ + "WCP1Hello", + "WCP2LoadUrl", + "WCP3Handshake", + "WCP4ValidateAppIdentity", + "WCP5ValidateAppIdentityFailedResponse", + "WCP5ValidateAppIdentityResponse", + ], +}; From 944cb8234bc649c21792afa860852249e28d9cb8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 15 May 2024 00:30:48 +0100 Subject: [PATCH 023/152] missed from last commit --- schemas/api/privateChanneladdEventListenerResponse.schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/schemas/api/privateChanneladdEventListenerResponse.schema.json b/schemas/api/privateChanneladdEventListenerResponse.schema.json index d75411f43..45564b339 100644 --- a/schemas/api/privateChanneladdEventListenerResponse.schema.json +++ b/schemas/api/privateChanneladdEventListenerResponse.schema.json @@ -6,7 +6,6 @@ "description": "A response to a privateChannelAddEventListener request.", "allOf": [ { - "$ref": "agentResponseMessage.schema.json" "$ref": "agentResponse.schema.json" }, { From 07f88864a472f0d7af2953d058cc506e8b0a472d Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 15 May 2024 18:23:41 +0100 Subject: [PATCH 024/152] adding getCurrentContext request/response --- schemas/api/agentResponse.schema.json | 1 + schemas/api/appRequest.schema.json | 1 + .../api/getCurrentContextRequest.schema.json | 56 ++++++++ .../api/getCurrentContextResponse.schema.json | 69 +++++++++ src/api/BrowserTypes.ts | 136 +++++++++++++++++- 5 files changed, 260 insertions(+), 3 deletions(-) create mode 100644 schemas/api/getCurrentContextRequest.schema.json create mode 100644 schemas/api/getCurrentContextResponse.schema.json diff --git a/schemas/api/agentResponse.schema.json b/schemas/api/agentResponse.schema.json index 4796e8afa..8eab915bf 100644 --- a/schemas/api/agentResponse.schema.json +++ b/schemas/api/agentResponse.schema.json @@ -19,6 +19,7 @@ "findIntentsByContextResponse", "getAppMetadataResponse", "getCurrentChannelResponse", + "getCurrentContextResponse", "getInfoResponse", "getOrCreateChannelResponse", "getUserChannelsResponse", diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index 9420c9cf6..d8a519ba1 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -19,6 +19,7 @@ "findIntentsByContextRequest", "getAppMetadataRequest", "getCurrentChannelRequest", + "getCurrentContextRequest", "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", diff --git a/schemas/api/getCurrentContextRequest.schema.json b/schemas/api/getCurrentContextRequest.schema.json new file mode 100644 index 000000000..508eb7631 --- /dev/null +++ b/schemas/api/getCurrentContextRequest.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getCurrentContextRequest.schema.json", + "type": "object", + "title": "GetCurrentContext Request", + "description": "A request to return the current context (either of a specified type or most recent broadcast) of a specified Channel. Returns `null` if no context (of the requested type if one was specified) is available in the channel.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetCurrentContextRequestType" + }, + "payload": { + "$ref": "#/$defs/GetCurrentContextRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetCurrentContextRequestType": { + "title": "GetCurrentContext Request Message Type", + "const": "getCurrentContextRequest" + }, + "GetCurrentContextRequestPayload": { + "title": "GetCurrentContext Request Payload", + "type": "object", + "properties": { + "channelId": { + "title": "Channel Id", + "description": "The id of the channel to return the current context of", + "type": "string" + }, + "contextType": { + "title": "Context type", + "description": "The type of context to return for OR `null` indicating that the most recently broadcast context on the channel should be returned.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": ["channelId", "contextType"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/getCurrentContextResponse.schema.json b/schemas/api/getCurrentContextResponse.schema.json new file mode 100644 index 000000000..55fd4a44e --- /dev/null +++ b/schemas/api/getCurrentContextResponse.schema.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/getCurrentContextResponse.schema.json", + "type": "object", + "title": "GetCurrentContext Response", + "description": "A response to a getCurrentContext request.", + "allOf": [ + { + "$ref": "agentResponse.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/GetCurrentContextResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/GetCurrentContextSuccessResponsePayload" + }, + { + "$ref": "#/$defs/GetCurrentContextErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "GetCurrentContextResponseType": { + "title": "GetCurrentContext Response Message Type", + "const": "getCurrentContextResponse" + }, + "GetCurrentContextSuccessResponsePayload": { + "title": "GetCurrentContext Response Payload", + "type": "object", + "properties": { + "context": { + "title": "Current Context", + "description": "The most recently broadcast context object (of the specified type, if one was specified), or `null` if none was available in the channel", + "oneOf": [ + { "$ref": "../context/context.schema.json" }, + { "type": "null" } + ] + } + }, + "required": [ + "context" + ], + "additionalProperties": false + }, + "GetCurrentContextErrorResponsePayload": { + "title": "GetCurrentContext Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "api.schema.json#/definitions/ChannelError" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 5693f3e85..d129ab3d9 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -28,6 +28,8 @@ // const getAppMetadataResponse = Convert.toGetAppMetadataResponse(json); // const getCurrentChannelRequest = Convert.toGetCurrentChannelRequest(json); // const getCurrentChannelResponse = Convert.toGetCurrentChannelResponse(json); +// const getCurrentContextRequest = Convert.toGetCurrentContextRequest(json); +// const getCurrentContextResponse = Convert.toGetCurrentContextResponse(json); // const getInfoRequest = Convert.toGetInfoRequest(json); // const getInfoResponse = Convert.toGetInfoResponse(json); // const getOrCreateChannelRequest = Convert.toGetOrCreateChannelRequest(json); @@ -145,7 +147,7 @@ export interface AppIdentifier { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the @@ -207,7 +209,7 @@ export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "Malforme * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; +export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; /** * A message from a Desktop Agent to an FDC3-enabled app representing an event. @@ -1362,6 +1364,92 @@ export interface GetCurrentChannelResponsePayload { channel?: Channel | null; } +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to return the current context (either of a specified type or most recent + * broadcast) of a specified Channel. Returns `null` if no context (of the requested type if + * one was specified) is available in the channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface GetCurrentContextRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: GetCurrentContextRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "getCurrentContextRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface GetCurrentContextRequestPayload { + /** + * The id of the channel to return the current context of + */ + channelId: string; + /** + * The type of context to return for OR `null` indicating that the most recently broadcast + * context on the channel should be returned. + */ + contextType: null | string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a getCurrentContext request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface GetCurrentContextResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: GetCurrentContextResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "getCurrentContextResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface GetCurrentContextResponsePayload { + error?: PurpleError; + /** + * The most recently broadcast context object (of the specified type, if one was specified), + * or `null` if none was available in the channel + */ + context?: null | Context; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -2979,6 +3067,22 @@ export class Convert { return JSON.stringify(uncast(value, r("GetCurrentChannelResponse")), null, 2); } + public static toGetCurrentContextRequest(json: string): GetCurrentContextRequest { + return cast(JSON.parse(json), r("GetCurrentContextRequest")); + } + + public static getCurrentContextRequestToJson(value: GetCurrentContextRequest): string { + return JSON.stringify(uncast(value, r("GetCurrentContextRequest")), null, 2); + } + + public static toGetCurrentContextResponse(json: string): GetCurrentContextResponse { + return cast(JSON.parse(json), r("GetCurrentContextResponse")); + } + + public static getCurrentContextResponseToJson(value: GetCurrentContextResponse): string { + return JSON.stringify(uncast(value, r("GetCurrentContextResponse")), null, 2); + } + public static toGetInfoRequest(json: string): GetInfoRequest { return cast(JSON.parse(json), r("GetInfoRequest")); } @@ -3681,6 +3785,24 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, { json: "channel", js: "channel", typ: u(undefined, u(r("Channel"), null)) }, ], false), + "GetCurrentContextRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("GetCurrentContextRequestPayload") }, + { json: "type", js: "type", typ: r("GetCurrentContextRequestType") }, + ], false), + "GetCurrentContextRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + { json: "contextType", js: "contextType", typ: u(null, "") }, + ], false), + "GetCurrentContextResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("GetCurrentContextResponsePayload") }, + { json: "type", js: "type", typ: r("GetCurrentContextResponseType") }, + ], false), + "GetCurrentContextResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, + { json: "context", js: "context", typ: u(undefined, u(null, r("Context"))) }, + ], false), "GetInfoRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("GetInfoRequestPayload") }, @@ -4002,6 +4124,7 @@ const typeMap: any = { "findIntentsByContextRequest", "getAppMetadataRequest", "getCurrentChannelRequest", + "getCurrentContextRequest", "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", @@ -4049,6 +4172,7 @@ const typeMap: any = { "findIntentsByContextResponse", "getAppMetadataResponse", "getCurrentChannelResponse", + "getCurrentContextResponse", "getInfoResponse", "getOrCreateChannelResponse", "getUserChannelsResponse", @@ -4169,6 +4293,12 @@ const typeMap: any = { "GetCurrentChannelResponseType": [ "getCurrentChannelResponse", ], + "GetCurrentContextRequestType": [ + "getCurrentContextRequest", + ], + "GetCurrentContextResponseType": [ + "getCurrentContextResponse", + ], "GetInfoRequestType": [ "getInfoRequest", ], From 803128d2c40ae6a1dc2ec82beb244a791b6255aa Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 15 May 2024 18:27:42 +0100 Subject: [PATCH 025/152] adding a broadcast event for the DA to use to communicate with listeners --- schemas/api/agentEvent.schema.json | 1 + schemas/api/broadcastEvent.schema.json | 51 ++++++++ schemas/api/broadcastRequest.schema.json | 2 +- src/api/BrowserTypes.ts | 147 ++++++++++++++++------- 4 files changed, 159 insertions(+), 42 deletions(-) create mode 100644 schemas/api/broadcastEvent.schema.json diff --git a/schemas/api/agentEvent.schema.json b/schemas/api/agentEvent.schema.json index 12129637b..4ba96bcf6 100644 --- a/schemas/api/agentEvent.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -9,6 +9,7 @@ "title": "Event Message Type", "type": "string", "enum": [ + "broadcastEvent", "channelChangedEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", diff --git a/schemas/api/broadcastEvent.schema.json b/schemas/api/broadcastEvent.schema.json new file mode 100644 index 000000000..b6a28fe7e --- /dev/null +++ b/schemas/api/broadcastEvent.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/broadcastEvent.schema.json", + "type": "object", + "title": "broadcast Event", + "description": "An event message from the Desktop Agent to an app indicating that context has been broadcast on a channel it is listening to.", + "allOf": [ + { + "$ref": "agentEvent.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/BroadcastEventType" + }, + "payload": { + "$ref": "#/$defs/BroadcastEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "BroadcastEventType": { + "title": "Broadcast Event Message Type", + "const": "broadcastEvent" + }, + "BroadcastEventPayload": { + "title": "broadcast Event Payload", + "type": "object", + "properties": { + "channelId": { + "title": "channel Id", + "description": "The Id of the channel that the broadcast was sent on.", + "type": "string" + }, + "context": { + "$ref": "../context/context.schema.json", + "title": "Context", + "description": "The context object that was broadcast." + } + }, + "additionalProperties": false, + "required": [ + "channelId", "context" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/broadcastRequest.schema.json b/schemas/api/broadcastRequest.schema.json index f196c5ab9..de85c7205 100644 --- a/schemas/api/broadcastRequest.schema.json +++ b/schemas/api/broadcastRequest.schema.json @@ -39,7 +39,7 @@ "context": { "$ref": "../context/context.schema.json", "title": "Context", - "description": "The context object that was the payload of a broadcast message." + "description": "The context object that is to be broadcast." } }, "additionalProperties": false, diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index d129ab3d9..33df84984 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -11,6 +11,7 @@ // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); // const addIntentListenerRequest = Convert.toAddIntentListenerRequest(json); // const addIntentListenerResponse = Convert.toAddIntentListenerResponse(json); +// const broadcastEvent = Convert.toBroadcastEvent(json); // const broadcastRequest = Convert.toBroadcastRequest(json); // const broadcastResponse = Convert.toBroadcastResponse(json); // const channelChangedEvent = Convert.toChannelChangedEvent(json); @@ -242,7 +243,7 @@ export interface AgentEventMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type EventMessageType = "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; +export type EventMessageType = "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; /** * A request to add a context listener to a specified Channel OR to the current user @@ -452,42 +453,53 @@ export type FluffyError = "MalformedContext" | "DesktopAgentNotFound" | "Resolve */ /** - * A request to broadcast context on a channel. + * An event message from the Desktop Agent to an app indicating that context has been + * broadcast on a channel it is listening to. * - * A request message from an FDC3-enabled app to a Desktop Agent. + * A message from a Desktop Agent to an FDC3-enabled app representing an event. */ -export interface BroadcastRequest { +export interface BroadcastEvent { /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddContextListenerRequestMeta; + meta: BroadcastEventMeta; /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload contains details of the event that the app is being notified about. */ - payload: BroadcastRequestPayload; + payload: BroadcastEventPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "broadcastRequest"; + type: "broadcastEvent"; } /** - * The message payload typically contains the arguments to FDC3 API functions. + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ -export interface BroadcastRequestPayload { +export interface BroadcastEventMeta { + eventUuid: string; + timestamp: Date; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface BroadcastEventPayload { /** - * The Id of the Channel that the broadcast was sent on + * The Id of the channel that the broadcast was sent on. */ channelId: string; /** - * The context object that was the payload of a broadcast message. + * The context object that was broadcast. */ context: Context; } /** - * The context object that was the payload of a broadcast message. + * The context object that was broadcast. + * + * The context object that is to be broadcast. * * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by * FDC3 operations. As such, it is not really meant to be used on its own, but is imported @@ -542,6 +554,46 @@ export interface Context { [property: string]: any; } +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to broadcast context on a channel. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface BroadcastRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: BroadcastRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "broadcastRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface BroadcastRequestPayload { + /** + * The Id of the Channel that the broadcast was sent on + */ + channelId: string; + /** + * The context object that is to be broadcast. + */ + context: Context; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. @@ -596,7 +648,7 @@ export interface ChannelChangedEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: ChannelChangedEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -608,14 +660,6 @@ export interface ChannelChangedEvent { type: "channelChangedEvent"; } -/** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ -export interface ChannelChangedEventMeta { - eventUuid: string; - timestamp: Date; -} - /** * The message payload contains details of the event that the app is being notified about. */ @@ -2187,7 +2231,7 @@ export interface PrivateChannelOnAddContextListenerEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: ChannelChangedEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2229,7 +2273,7 @@ export interface PrivateChannelOnDisconnectEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: ChannelChangedEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2266,7 +2310,7 @@ export interface PrivateChannelOnUnsubscribeEventEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: ChannelChangedEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2931,6 +2975,14 @@ export class Convert { return JSON.stringify(uncast(value, r("AddIntentListenerResponse")), null, 2); } + public static toBroadcastEvent(json: string): BroadcastEvent { + return cast(JSON.parse(json), r("BroadcastEvent")); + } + + public static broadcastEventToJson(value: BroadcastEvent): string { + return JSON.stringify(uncast(value, r("BroadcastEvent")), null, 2); + } + public static toBroadcastRequest(json: string): BroadcastRequest { return cast(JSON.parse(json), r("BroadcastRequest")); } @@ -3593,12 +3645,16 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("FluffyError")) }, { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, ], "any"), - "BroadcastRequest": o([ - { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, - { json: "payload", js: "payload", typ: r("BroadcastRequestPayload") }, - { json: "type", js: "type", typ: r("BroadcastRequestType") }, + "BroadcastEvent": o([ + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastEventPayload") }, + { json: "type", js: "type", typ: r("BroadcastEventType") }, ], false), - "BroadcastRequestPayload": o([ + "BroadcastEventMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "BroadcastEventPayload": o([ { json: "channelId", js: "channelId", typ: "" }, { json: "context", js: "context", typ: r("Context") }, ], false), @@ -3607,6 +3663,15 @@ const typeMap: any = { { json: "name", js: "name", typ: u(undefined, "") }, { json: "type", js: "type", typ: "" }, ], "any"), + "BroadcastRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastRequestPayload") }, + { json: "type", js: "type", typ: r("BroadcastRequestType") }, + ], false), + "BroadcastRequestPayload": o([ + { json: "channelId", js: "channelId", typ: "" }, + { json: "context", js: "context", typ: r("Context") }, + ], false), "BroadcastResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, @@ -3616,14 +3681,10 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, ], "any"), "ChannelChangedEvent": o([ - { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("ChannelChangedEventPayload") }, { json: "type", js: "type", typ: r("ChannelChangedEventType") }, ], false), - "ChannelChangedEventMeta": o([ - { json: "eventUuid", js: "eventUuid", typ: "" }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "ChannelChangedEventPayload": o([ { json: "newChannelId", js: "newChannelId", typ: u(null, "") }, ], false), @@ -3960,7 +4021,7 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, ], false), "PrivateChannelOnAddContextListenerEvent": o([ - { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnAddContextListenerEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnAddContextListenerEventType") }, ], false), @@ -3969,7 +4030,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnDisconnectEvent": o([ - { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnDisconnectEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnDisconnectEventType") }, ], false), @@ -3977,7 +4038,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnUnsubscribeEventEvent": o([ - { json: "meta", js: "meta", typ: r("ChannelChangedEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventEventType") }, ], false), @@ -4188,6 +4249,7 @@ const typeMap: any = { "raiseIntentResultResponse", ], "EventMessageType": [ + "broadcastEvent", "channelChangedEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", @@ -4222,6 +4284,9 @@ const typeMap: any = { "AddIntentListenerResponseType": [ "addIntentListenerResponse", ], + "BroadcastEventType": [ + "broadcastEvent", + ], "BroadcastRequestType": [ "broadcastRequest", ], From 0ef87c804773f520a96cff75084ef8d74577ea61 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 24 May 2024 18:01:08 +0100 Subject: [PATCH 026/152] Adding first pass on iframe messages --- schemas/api/iFrameHandshake.schema.json | 44 +++++++++++ schemas/api/iFrameHello.schema.json | 44 +++++++++++ schemas/api/iFrameMessage.schema.json | 34 +++++++++ schemas/api/iFrameResolve.schema.json | 53 +++++++++++++ schemas/api/iFrameResolveAction.schema.json | 85 +++++++++++++++++++++ 5 files changed, 260 insertions(+) create mode 100644 schemas/api/iFrameHandshake.schema.json create mode 100644 schemas/api/iFrameHello.schema.json create mode 100644 schemas/api/iFrameMessage.schema.json create mode 100644 schemas/api/iFrameResolve.schema.json create mode 100644 schemas/api/iFrameResolveAction.schema.json diff --git a/schemas/api/iFrameHandshake.schema.json b/schemas/api/iFrameHandshake.schema.json new file mode 100644 index 000000000..83f48c9aa --- /dev/null +++ b/schemas/api/iFrameHandshake.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", + "title": "iframe Handshake", + "description": "Handshake message sent back by an iframe to the DA proxy code indicating that it is setup and ready to communicate over the MessagePort.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeHandshakeBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeHandshakeBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeHandshake Message Type", + "const": "iframeHandshake" + }, + "payload": { + "title": "iframeHandshake Payload", + "type": "object", + "properties": { + "resolverDetails": { + "title": "FDC3 version", + "type": "string", + "description": "Implementation details, such as vendor and version details, for logging purposes." + } + }, + "additionalProperties": false, + "required": ["resolverDetails"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json new file mode 100644 index 000000000..08919f74a --- /dev/null +++ b/schemas/api/iFrameHello.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", + "title": "iframe Handshake", + "description": "Handshake message sent by the DA proxy code in getAgent() to an iframe, that it has injected into the page, with a MessagePort appended that should be used for subsequent communication steps.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeHandshakeBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeHandshakeBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeHandshake Message Type", + "const": "iframeHandshake" + }, + "payload": { + "title": "iframeHandshake Payload", + "type": "object", + "properties": { + "fdc3Version": { + "title": "FDC3 version", + "type": "string", + "description": "The version of FDC3 API that the Desktop Agent proxy is providing support for." + } + }, + "additionalProperties": false, + "required": ["fdc3Version"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/iFrameMessage.schema.json b/schemas/api/iFrameMessage.schema.json new file mode 100644 index 000000000..0a8778ec8 --- /dev/null +++ b/schemas/api/iFrameMessage.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json", + "title": "iframe Message", + "type": "object", + "description": "A message used to communicate with iframes injected by `getAgent()` for displaying UI elements such as the intent resolver or channel selector. Used for messages sent in either direction.", + "properties": { + "type": { + "title": "iframe Message type", + "type": "string", + "enum": [ + "iframeHello", + "iframeHandshake", + "iframeResolve", + "iframeResolveAction", + "iframeChannels", + "iframeChannelSelected", + "iframeChannelToggle", + "iframeChannelDrag" + ], + "description": "Identifies the type of the message to or from the iframe." + }, + "payload": { + "title": "Message payload", + "type": "object", + "description": "The message payload", + "additionalProperties": true + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/schemas/api/iFrameResolve.schema.json b/schemas/api/iFrameResolve.schema.json new file mode 100644 index 000000000..6e62d6b5e --- /dev/null +++ b/schemas/api/iFrameResolve.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeResolve.schema.json", + "title": "iframe Resolve", + "description": "Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an iframe with the resolver data to setup the UI.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeResolveBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeResolveBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeResolve Message Type", + "const": "iframeResolve" + }, + "payload": { + "title": "iframeResolve Payload", + "type": "object", + "properties": { + "context": { + "$ref": "../context/context.schema.json", + "title": "Context" + }, + "appIntents": { + "title": "Resolution options", + "type": "array", + "description": "An array of AppIntent objects defining the resolution options.", + "items": { + "$ref": "api.schema.json#/definitions/AppIntent" + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["context", "appIntents"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/schemas/api/iFrameResolveAction.schema.json b/schemas/api/iFrameResolveAction.schema.json new file mode 100644 index 000000000..16af8c1ce --- /dev/null +++ b/schemas/api/iFrameResolveAction.schema.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeResolveAction.schema.json", + "title": "iframe Resolve", + "description": "Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an iframe with the resolver data to setup the UI.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeResolveActionBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeResolveActionBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeResolveAction Message Type", + "const": "iframeResolveAction" + }, + "payload": { + "oneOf": [ + { "$ref": "#/$defs/iframeResolveActionPayload"}, + { "$ref": "#/$defs/iframeResolveCancelPayload"} + ] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + }, + "iframeResolveActionPayload": { + "title": "iframeResolveAction Payload", + "type": "object", + "properties": { + "intent": { + "title": "Intent name", + "type": "string", + "description": "The intent resolved" + }, + "appIdentifier": { + "title": "AppIdentifier", + "description": "The App resolution option chosen", + "$ref": "api.schema.json#/definitions/AppIdentifier" + }, + "action": { + "anyOf": [ + { + "type": "string", + "const": "hover" + }, + { + "type": "string", + "const": "click" + } + ] + } + }, + "required": [ + "intent", + "appIdentifier", + "action" + ], + "additionalProperties": false + }, + "iframeResolveCancelPayload": { + "title": "iframeResolveCancel Payload", + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "cancel" + } + }, + "required": [ + "action" + ], + "additionalProperties": false + } + } +} \ No newline at end of file From ccdfa296b0111ace9bac7460c2ffa7d68dda8d37 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 24 May 2024 18:06:23 +0100 Subject: [PATCH 027/152] adjustments to resolver messages --- schemas/api/iFrameHandshake.schema.json | 8 +- schemas/api/iFrameHello.schema.json | 16 +- schemas/api/iFrameResolveAction.schema.json | 8 +- src/api/BrowserTypes.ts | 356 +++++++++++++++++++- 4 files changed, 366 insertions(+), 22 deletions(-) diff --git a/schemas/api/iFrameHandshake.schema.json b/schemas/api/iFrameHandshake.schema.json index 83f48c9aa..d79313b35 100644 --- a/schemas/api/iFrameHandshake.schema.json +++ b/schemas/api/iFrameHandshake.schema.json @@ -24,14 +24,14 @@ "title": "iframeHandshake Payload", "type": "object", "properties": { - "resolverDetails": { - "title": "FDC3 version", + "implementationDetails": { + "title": "Implementation Details", "type": "string", - "description": "Implementation details, such as vendor and version details, for logging purposes." + "description": "Details about the UI implementation in the iframe, such as vendor and version, for logging purposes." } }, "additionalProperties": false, - "required": ["resolverDetails"] + "required": ["implementationDetails"] } }, "required": [ diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index 08919f74a..a7cdb0332 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", - "title": "iframe Handshake", - "description": "Handshake message sent by the DA proxy code in getAgent() to an iframe, that it has injected into the page, with a MessagePort appended that should be used for subsequent communication steps.", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeHello.schema.json", + "title": "iframe Hello", + "description": "Hello message sent by the DA proxy code in getAgent() to an iframe, that it has injected into the page, with a MessagePort appended that should be used for subsequent communication steps.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeHandshakeBase" + "$ref": "#/$defs/IframeHelloBase" }, { "$ref": "iFrameMessage.schema.json" } ], "$defs": { - "IframeHandshakeBase": { + "IframeHelloBase": { "type": "object", "properties": { "type": { - "title": "iframeHandshake Message Type", - "const": "iframeHandshake" + "title": "iframeHello Message Type", + "const": "iframeHello" }, "payload": { - "title": "iframeHandshake Payload", + "title": "iframeHello Payload", "type": "object", "properties": { "fdc3Version": { diff --git a/schemas/api/iFrameResolveAction.schema.json b/schemas/api/iFrameResolveAction.schema.json index 16af8c1ce..1728c8c2a 100644 --- a/schemas/api/iFrameResolveAction.schema.json +++ b/schemas/api/iFrameResolveAction.schema.json @@ -1,8 +1,8 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeResolveAction.schema.json", - "title": "iframe Resolve", - "description": "Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an iframe with the resolver data to setup the UI.", + "title": "iframe Resolve Action", + "description": "Message from an intent resolver UI in an iframe to DA proxy code in getAgent() reporting a user action.", "type": "object", "allOf": [ { @@ -34,7 +34,7 @@ "additionalProperties": false }, "iframeResolveActionPayload": { - "title": "iframeResolveAction Payload", + "title": "iframeResolve Action Payload", "type": "object", "properties": { "intent": { @@ -68,7 +68,7 @@ "additionalProperties": false }, "iframeResolveCancelPayload": { - "title": "iframeResolveCancel Payload", + "title": "iframeResolve Cancel Payload", "type": "object", "properties": { "action": { diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 33df84984..64f8d5504 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -37,6 +37,12 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const iframeHandshake = Convert.toIframeHandshake(json); +// const iframeHello = Convert.toIframeHello(json); +// const iframeMessage = Convert.toIframeMessage(json); +// const iframeResolve = Convert.toIframeResolve(json); +// const iframeResolveAction = Convert.toIframeResolveAction(json); +// const intentEvent = Convert.toIntentEvent(json); // const intentListenerUnsubscribeRequest = Convert.toIntentListenerUnsubscribeRequest(json); // const intentListenerUnsubscribeResponse = Convert.toIntentListenerUnsubscribeResponse(json); // const joinUserChannelRequest = Convert.toJoinUserChannelRequest(json); @@ -121,6 +127,10 @@ export interface AppRequestMessageMeta { * Field that represents the source application that the request being responded to was * received from, for debugging purposes. * + * The App resolution option chosen + * + * Details of the application instance that raised the intent + * * Identifier for the app instance that was selected (or started) to resolve the intent. * `source.instanceId` MUST be set, indicating the specific app instance that * received the intent. @@ -243,7 +253,7 @@ export interface AgentEventMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type EventMessageType = "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; +export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; /** * A request to add a context listener to a specified Channel OR to the current user @@ -501,6 +511,8 @@ export interface BroadcastEventPayload { * * The context object that is to be broadcast. * + * The context object passed with the raised intent. + * * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by * FDC3 operations. As such, it is not really meant to be used on its own, but is imported * by more specific type definitions (standardized or custom) to provide the structure and @@ -1771,6 +1783,214 @@ export interface GetUserChannelsResponsePayload { userChannels?: Channel[]; } +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * Handshake message sent back by an iframe to the DA proxy code indicating that it is setup + * and ready to communicate over the MessagePort. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeHandshake { + /** + * The message payload + */ + payload: IframeHandshakePayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeHandshake"; +} + +/** + * The message payload + */ +export interface IframeHandshakePayload { + /** + * Details about the UI implementation in the iframe, such as vendor and version, for + * logging purposes. + */ + implementationDetails: string; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * Hello message sent by the DA proxy code in getAgent() to an iframe, that it has injected + * into the page, with a MessagePort appended that should be used for subsequent + * communication steps. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeHello { + /** + * The message payload + */ + payload: IframeHelloPayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeHello"; +} + +/** + * The message payload + */ +export interface IframeHelloPayload { + /** + * The version of FDC3 API that the Desktop Agent proxy is providing support for. + */ + fdc3Version: string; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeMessage { + /** + * The message payload + */ + payload?: { [key: string]: any }; + /** + * Identifies the type of the message to or from the iframe. + */ + type: IframeMessageType; +} + +/** + * Identifies the type of the message to or from the iframe. + */ +export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected" | "iframeChannelToggle" | "iframeChannelDrag"; + +/** + * Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an + * iframe with the resolver data to setup the UI. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeResolve { + /** + * The message payload + */ + payload: IframeResolvePayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeResolve"; +} + +/** + * The message payload + */ +export interface IframeResolvePayload { + /** + * An array of AppIntent objects defining the resolution options. + */ + appIntents: AppIntent[]; + context: Context; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * Message from an intent resolver UI in an iframe to DA proxy code in getAgent() reporting + * a user action. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeResolveAction { + /** + * The message payload + */ + payload: IframeResolveActionPayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeResolveAction"; +} + +/** + * The message payload + */ +export interface IframeResolveActionPayload { + action: Action; + /** + * The App resolution option chosen + */ + appIdentifier?: AppIdentifier; + /** + * The intent resolved + */ + intent?: string; +} + +export type Action = "hover" | "click" | "cancel"; + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * An event message from the Desktop Agent to an app indicating that it has been selected to + * resolve a raised intent and context. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface IntentEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: BroadcastEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: IntentEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "intentEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface IntentEventPayload { + /** + * The context object passed with the raised intent. + */ + context: Context; + /** + * The intent that was raised. + */ + intent: string; + /** + * Details of the application instance that raised the intent + */ + originatingApp: AppIdentifier; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -2796,7 +3016,7 @@ export interface WCP4ValidateAppIdentity { /** * The message payload, containing data pertaining to this connection step. */ - payload: PayloadClass; + payload: WCP4ValidateAppIdentityPayload; /** * Identifies the type of the connection step message. */ @@ -2806,7 +3026,7 @@ export interface WCP4ValidateAppIdentity { /** * The message payload, containing data pertaining to this connection step. */ -export interface PayloadClass { +export interface WCP4ValidateAppIdentityPayload { /** * URL for an App Directory record that provides identity details for the application * attempting to connect @@ -3183,6 +3403,54 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } + public static toIframeHandshake(json: string): IframeHandshake { + return cast(JSON.parse(json), r("IframeHandshake")); + } + + public static iframeHandshakeToJson(value: IframeHandshake): string { + return JSON.stringify(uncast(value, r("IframeHandshake")), null, 2); + } + + public static toIframeHello(json: string): IframeHello { + return cast(JSON.parse(json), r("IframeHello")); + } + + public static iframeHelloToJson(value: IframeHello): string { + return JSON.stringify(uncast(value, r("IframeHello")), null, 2); + } + + public static toIframeMessage(json: string): IframeMessage { + return cast(JSON.parse(json), r("IframeMessage")); + } + + public static iframeMessageToJson(value: IframeMessage): string { + return JSON.stringify(uncast(value, r("IframeMessage")), null, 2); + } + + public static toIframeResolve(json: string): IframeResolve { + return cast(JSON.parse(json), r("IframeResolve")); + } + + public static iframeResolveToJson(value: IframeResolve): string { + return JSON.stringify(uncast(value, r("IframeResolve")), null, 2); + } + + public static toIframeResolveAction(json: string): IframeResolveAction { + return cast(JSON.parse(json), r("IframeResolveAction")); + } + + public static iframeResolveActionToJson(value: IframeResolveAction): string { + return JSON.stringify(uncast(value, r("IframeResolveAction")), null, 2); + } + + public static toIntentEvent(json: string): IntentEvent { + return cast(JSON.parse(json), r("IntentEvent")); + } + + public static intentEventToJson(value: IntentEvent): string { + return JSON.stringify(uncast(value, r("IntentEvent")), null, 2); + } + public static toIntentListenerUnsubscribeRequest(json: string): IntentListenerUnsubscribeRequest { return cast(JSON.parse(json), r("IntentListenerUnsubscribeRequest")); } @@ -3925,6 +4193,51 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), + "IframeHandshake": o([ + { json: "payload", js: "payload", typ: r("IframeHandshakePayload") }, + { json: "type", js: "type", typ: r("IframeHandshakeType") }, + ], false), + "IframeHandshakePayload": o([ + { json: "implementationDetails", js: "implementationDetails", typ: "" }, + ], false), + "IframeHello": o([ + { json: "payload", js: "payload", typ: r("IframeHelloPayload") }, + { json: "type", js: "type", typ: r("IframeHelloType") }, + ], false), + "IframeHelloPayload": o([ + { json: "fdc3Version", js: "fdc3Version", typ: "" }, + ], false), + "IframeMessage": o([ + { json: "payload", js: "payload", typ: u(undefined, m("any")) }, + { json: "type", js: "type", typ: r("IframeMessageType") }, + ], false), + "IframeResolve": o([ + { json: "payload", js: "payload", typ: r("IframeResolvePayload") }, + { json: "type", js: "type", typ: r("IframeResolveType") }, + ], false), + "IframeResolvePayload": o([ + { json: "appIntents", js: "appIntents", typ: a(r("AppIntent")) }, + { json: "context", js: "context", typ: r("Context") }, + ], false), + "IframeResolveAction": o([ + { json: "payload", js: "payload", typ: r("IframeResolveActionPayload") }, + { json: "type", js: "type", typ: r("IframeResolveActionType") }, + ], false), + "IframeResolveActionPayload": o([ + { json: "action", js: "action", typ: r("Action") }, + { json: "appIdentifier", js: "appIdentifier", typ: u(undefined, r("AppIdentifier")) }, + { json: "intent", js: "intent", typ: u(undefined, "") }, + ], false), + "IntentEvent": o([ + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "payload", js: "payload", typ: r("IntentEventPayload") }, + { json: "type", js: "type", typ: r("IntentEventType") }, + ], false), + "IntentEventPayload": o([ + { json: "context", js: "context", typ: r("Context") }, + { json: "intent", js: "intent", typ: "" }, + { json: "originatingApp", js: "originatingApp", typ: r("AppIdentifier") }, + ], false), "IntentListenerUnsubscribeRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("IntentListenerUnsubscribeRequestPayload") }, @@ -4149,10 +4462,10 @@ const typeMap: any = { ], false), "WCP4ValidateAppIdentity": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("PayloadClass") }, + { json: "payload", js: "payload", typ: r("WCP4ValidateAppIdentityPayload") }, { json: "type", js: "type", typ: r("WCP4ValidateAppIdentityType") }, ], false), - "PayloadClass": o([ + "WCP4ValidateAppIdentityPayload": o([ { json: "appDUrl", js: "appDUrl", typ: u(undefined, "") }, { json: "appId", js: "appId", typ: "" }, { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, @@ -4251,6 +4564,7 @@ const typeMap: any = { "EventMessageType": [ "broadcastEvent", "channelChangedEvent", + "intentEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", "privateChannelOnUnsubscribeEvent", @@ -4382,6 +4696,36 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], + "IframeHandshakeType": [ + "iframeHandshake", + ], + "IframeHelloType": [ + "iframeHello", + ], + "IframeMessageType": [ + "iframeChannelDrag", + "iframeChannelSelected", + "iframeChannelToggle", + "iframeChannels", + "iframeHandshake", + "iframeHello", + "iframeResolve", + "iframeResolveAction", + ], + "IframeResolveType": [ + "iframeResolve", + ], + "Action": [ + "cancel", + "click", + "hover", + ], + "IframeResolveActionType": [ + "iframeResolveAction", + ], + "IntentEventType": [ + "intentEvent", + ], "IntentListenerUnsubscribeRequestType": [ "intentListenerUnsubscribeRequest", ], From 46e00b07ca30029407119792fcb9861483403947 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 24 May 2024 18:07:34 +0100 Subject: [PATCH 028/152] Adding event message to feed intents raised to handlers for addIntentListener --- schemas/api/agentEvent.schema.json | 1 + schemas/api/intentEvent.schema.json | 56 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 schemas/api/intentEvent.schema.json diff --git a/schemas/api/agentEvent.schema.json b/schemas/api/agentEvent.schema.json index 4ba96bcf6..d36797b7b 100644 --- a/schemas/api/agentEvent.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -9,6 +9,7 @@ "title": "Event Message Type", "type": "string", "enum": [ + "intentEvent", "broadcastEvent", "channelChangedEvent", "privateChannelOnAddContextListenerEvent", diff --git a/schemas/api/intentEvent.schema.json b/schemas/api/intentEvent.schema.json new file mode 100644 index 000000000..b92d0ed30 --- /dev/null +++ b/schemas/api/intentEvent.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/intentEvent.schema.json", + "type": "object", + "title": "intent Event", + "description": "An event message from the Desktop Agent to an app indicating that it has been selected to resolve a raised intent and context.", + "allOf": [ + { + "$ref": "agentEvent.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/IntentEventType" + }, + "payload": { + "$ref": "#/$defs/IntentEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "IntentEventType": { + "title": "Intent Event Message Type", + "const": "intentEvent" + }, + "IntentEventPayload": { + "title": "Intent Event Payload", + "type": "object", + "properties": { + "intent": { + "title": "Intent", + "description": "The intent that was raised.", + "type": "string" + }, + "context": { + "$ref": "../context/context.schema.json", + "title": "Context", + "description": "The context object passed with the raised intent." + }, + "originatingApp": { + "title": "Originating AppIdentifier", + "description": "Details of the application instance that raised the intent", + "$ref": "api.schema.json#/definitions/AppIdentifier" + } + }, + "additionalProperties": false, + "required": [ + "intent", "context", "originatingApp" + ] + } + } +} \ No newline at end of file From e7249551ec22ce68e1fd9806b734a49a7c5648a1 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 24 May 2024 18:25:30 +0100 Subject: [PATCH 029/152] First channel selector messages (setup and selection) --- schemas/api/iFrameChannelSelected.schema.json | 47 +++++++ schemas/api/iFrameChannels.schema.json | 70 ++++++++++ src/api/BrowserTypes.ts | 132 +++++++++++++++++- 3 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 schemas/api/iFrameChannelSelected.schema.json create mode 100644 schemas/api/iFrameChannels.schema.json diff --git a/schemas/api/iFrameChannelSelected.schema.json b/schemas/api/iFrameChannelSelected.schema.json new file mode 100644 index 000000000..f88484aa2 --- /dev/null +++ b/schemas/api/iFrameChannelSelected.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelSelected.schema.json", + "title": "iframe Channel Selected", + "description": "Message from the channel selector UI to the DA proxy sent when the channel selection changes.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeChannelSelectedBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeChannelSelectedBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeChannelSelected Message Type", + "const": "iframeChannelSelected" + }, + "payload": { + "title": "iframeChannelSelected Payload", + "type": "object", + "properties": { + "selected": { + "title": "Selected Channel", + "description": "The id of the channel that should be currently selected, or `null` if none should be selected", + "oneOf": [ + {"type": "string"}, {"type": "null"} + ] + } + }, + "additionalProperties": false, + "required": ["selected"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/schemas/api/iFrameChannels.schema.json b/schemas/api/iFrameChannels.schema.json new file mode 100644 index 000000000..0712073e0 --- /dev/null +++ b/schemas/api/iFrameChannels.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannels.schema.json", + "title": "iframe Channels", + "description": "Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an iframe with the channel definitions and current channel selection.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeChannelsBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeChannelsBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeChannels Message Type", + "const": "iframeChannels" + }, + "payload": { + "title": "iframeChannels Payload", + "type": "object", + "properties": { + "userChannels": { + "title": "User Channels", + "description": "User Channel definitions", + "type": "array", + "items": { + "$ref": "api.schema.json#/definitions/Channel" + } + }, + "selected": { + "title": "Selected Channel", + "description": "The id of the channel taht should be currently selected, or `null` if none should be selected", + "oneOf": [ + {"type": "string"}, {"type": "null"} + ] + }, + "location": { + "title": "Location", + "description": "If the channel selector was previously displayed in this window its location may be restored by setting the location coordinates", + "type": "object", + "properties": { + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "required": ["x", "y"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["userChannels", "selected"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 64f8d5504..83675d268 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -37,6 +37,8 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const iframeChannels = Convert.toIframeChannels(json); +// const iframeChannelSelected = Convert.toIframeChannelSelected(json); // const iframeHandshake = Convert.toIframeHandshake(json); // const iframeHello = Convert.toIframeHello(json); // const iframeMessage = Convert.toIframeMessage(json); @@ -1788,6 +1790,92 @@ export interface GetUserChannelsResponsePayload { * the message relates to, e.g. 'findIntent', with 'Response' appended. */ +/** + * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an + * iframe with the channel definitions and current channel selection. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeChannels { + /** + * The message payload + */ + payload: IframeChannelsPayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeChannels"; +} + +/** + * The message payload + */ +export interface IframeChannelsPayload { + /** + * If the channel selector was previously displayed in this window its location may be + * restored by setting the location coordinates + */ + location?: Location; + /** + * The id of the channel taht should be currently selected, or `null` if none should be + * selected + */ + selected: null | string; + /** + * User Channel definitions + */ + userChannels: Channel[]; +} + +/** + * If the channel selector was previously displayed in this window its location may be + * restored by setting the location coordinates + */ +export interface Location { + x: number; + y: number; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * Message from the channel selector UI to the DA proxy sent when the channel selection + * changes. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeChannelSelected { + /** + * The message payload + */ + payload: IframeChannelSelectedPayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeChannelSelected"; +} + +/** + * The message payload + */ +export interface IframeChannelSelectedPayload { + /** + * The id of the channel that should be currently selected, or `null` if none should be + * selected + */ + selected: null | string; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + /** * Handshake message sent back by an iframe to the DA proxy code indicating that it is setup * and ready to communicate over the MessagePort. @@ -3403,6 +3491,22 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } + public static toIframeChannels(json: string): IframeChannels { + return cast(JSON.parse(json), r("IframeChannels")); + } + + public static iframeChannelsToJson(value: IframeChannels): string { + return JSON.stringify(uncast(value, r("IframeChannels")), null, 2); + } + + public static toIframeChannelSelected(json: string): IframeChannelSelected { + return cast(JSON.parse(json), r("IframeChannelSelected")); + } + + public static iframeChannelSelectedToJson(value: IframeChannelSelected): string { + return JSON.stringify(uncast(value, r("IframeChannelSelected")), null, 2); + } + public static toIframeHandshake(json: string): IframeHandshake { return cast(JSON.parse(json), r("IframeHandshake")); } @@ -4193,6 +4297,26 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), + "IframeChannels": o([ + { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, + { json: "type", js: "type", typ: r("IframeChannelsType") }, + ], false), + "IframeChannelsPayload": o([ + { json: "location", js: "location", typ: u(undefined, r("Location")) }, + { json: "selected", js: "selected", typ: u(null, "") }, + { json: "userChannels", js: "userChannels", typ: a(r("Channel")) }, + ], false), + "Location": o([ + { json: "x", js: "x", typ: 0 }, + { json: "y", js: "y", typ: 0 }, + ], false), + "IframeChannelSelected": o([ + { json: "payload", js: "payload", typ: r("IframeChannelSelectedPayload") }, + { json: "type", js: "type", typ: r("IframeChannelSelectedType") }, + ], false), + "IframeChannelSelectedPayload": o([ + { json: "selected", js: "selected", typ: u(null, "") }, + ], false), "IframeHandshake": o([ { json: "payload", js: "payload", typ: r("IframeHandshakePayload") }, { json: "type", js: "type", typ: r("IframeHandshakeType") }, @@ -4696,6 +4820,12 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], + "IframeChannelsType": [ + "iframeChannels", + ], + "IframeChannelSelectedType": [ + "iframeChannelSelected", + ], "IframeHandshakeType": [ "iframeHandshake", ], From 903262b716a794aa5832abf6aab9c7ab8424f589 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 24 May 2024 18:47:00 +0100 Subject: [PATCH 030/152] Adding drag and resize messages for channel selector --- schemas/api/iFrameChannelDrag.schema.json | 55 +++++++ schemas/api/iFrameChannelResize.schema.json | 70 +++++++++ schemas/api/iFrameChannels.schema.json | 2 +- schemas/api/iFrameMessage.schema.json | 2 +- src/api/BrowserTypes.ts | 164 +++++++++++++++++++- 5 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 schemas/api/iFrameChannelDrag.schema.json create mode 100644 schemas/api/iFrameChannelResize.schema.json diff --git a/schemas/api/iFrameChannelDrag.schema.json b/schemas/api/iFrameChannelDrag.schema.json new file mode 100644 index 000000000..eb52b6978 --- /dev/null +++ b/schemas/api/iFrameChannelDrag.schema.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelDrag.schema.json", + "title": "iframe Channel Drag", + "description": "Message from the channel selector UI to the DA proxy when the user drags the selector to a new location.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeChannelDragBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeChannelDragBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeChannelDrag Message Type", + "const": "iframeChannelDrag" + }, + "payload": { + "title": "iframeChannelDrag Payload", + "type": "object", + "properties": { + "mouse": { + "title": "Dimensions", + "description": "The offset to move the frame by", + "type": "object", + "properties": { + "offsetX": { + "type": "integer" + }, + "offsetY": { + "type": "integer" + } + }, + "required": ["offsetX", "offsetY"], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["mouse"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/schemas/api/iFrameChannelResize.schema.json b/schemas/api/iFrameChannelResize.schema.json new file mode 100644 index 000000000..bd221eebb --- /dev/null +++ b/schemas/api/iFrameChannelResize.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelResize.schema.json", + "title": "iframe Channel Resize", + "description": "Message from the channel selector UI to the DA proxy when the user hits a toggle button that opens or closes the selector or otherwise resizes it. Includes the size that it should change to and the corner (if any) at which the change should be made.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeChannelResizeBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeChannelResizeBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeChannelResize Message Type", + "const": "iframeChannelResize" + }, + "payload": { + "title": "iframeChannelResize Payload", + "type": "object", + "properties": { + "dimensions": { + "title": "Dimensions", + "description": "The updated dimensions of the UI", + "type": "object", + "properties": { + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + }, + "required": ["width", "height"], + "additionalProperties": false + }, + "resizeAnchor": { + "title": "Resizing", + "description": "When resizing anchor at the indicated location,\ne.g.\n\t- for top-left and a larger size: the bottom right corner should move down and out.\n\t- for top and smaller size: both the bottom corners should move in and up.", + "anyOf": [ + { "type": "string", "const": "top-left" }, + { "type": "string", "const": "top" }, + { "type": "string", "const": "top-right" }, + { "type": "string", "const": "right" }, + { "type": "string", "const": "bottom-right" }, + { "type": "string", "const": "bottom" }, + { "type": "string", "const": "bottom-left" }, + { "type": "string", "const": "left" }, + { "type": "string", "const": "center" } + ] + } + }, + "additionalProperties": false, + "required": ["dimensions", "resizeAnchor"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/schemas/api/iFrameChannels.schema.json b/schemas/api/iFrameChannels.schema.json index 0712073e0..9f5c69acc 100644 --- a/schemas/api/iFrameChannels.schema.json +++ b/schemas/api/iFrameChannels.schema.json @@ -34,7 +34,7 @@ }, "selected": { "title": "Selected Channel", - "description": "The id of the channel taht should be currently selected, or `null` if none should be selected", + "description": "The id of the channel that should be currently selected, or `null` if none should be selected", "oneOf": [ {"type": "string"}, {"type": "null"} ] diff --git a/schemas/api/iFrameMessage.schema.json b/schemas/api/iFrameMessage.schema.json index 0a8778ec8..aaad9b99b 100644 --- a/schemas/api/iFrameMessage.schema.json +++ b/schemas/api/iFrameMessage.schema.json @@ -15,7 +15,7 @@ "iframeResolveAction", "iframeChannels", "iframeChannelSelected", - "iframeChannelToggle", + "iframeChannelResize", "iframeChannelDrag" ], "description": "Identifies the type of the message to or from the iframe." diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 83675d268..b6e2bf88c 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -37,6 +37,8 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const iframeChannelDrag = Convert.toIframeChannelDrag(json); +// const iframeChannelResize = Convert.toIframeChannelResize(json); // const iframeChannels = Convert.toIframeChannels(json); // const iframeChannelSelected = Convert.toIframeChannelSelected(json); // const iframeHandshake = Convert.toIframeHandshake(json); @@ -1790,6 +1792,104 @@ export interface GetUserChannelsResponsePayload { * the message relates to, e.g. 'findIntent', with 'Response' appended. */ +/** + * Message from the channel selector UI to the DA proxy when the user drags the selector to + * a new location. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeChannelDrag { + /** + * The message payload + */ + payload: IframeChannelDragPayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeChannelDrag"; +} + +/** + * The message payload + */ +export interface IframeChannelDragPayload { + /** + * The offset to move the frame by + */ + mouse: MouseClass; +} + +/** + * The offset to move the frame by + */ +export interface MouseClass { + offsetX: number; + offsetY: number; +} + +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * Message from the channel selector UI to the DA proxy when the user hits a toggle button + * that opens or closes the selector or otherwise resizes it. Includes the size that it + * should change to and the corner (if any) at which the change should be made. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeChannelResize { + /** + * The message payload + */ + payload: IframeChannelResizePayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeChannelResize"; +} + +/** + * The message payload + */ +export interface IframeChannelResizePayload { + /** + * The updated dimensions of the UI + */ + dimensions: DimensionsClass; + /** + * When resizing anchor at the indicated location, + * e.g. + * - for top-left and a larger size: the bottom right corner should move down and out. + * - for top and smaller size: both the bottom corners should move in and up. + */ + resizeAnchor: Resizing; +} + +/** + * The updated dimensions of the UI + */ +export interface DimensionsClass { + height: number; + width: number; +} + +/** + * When resizing anchor at the indicated location, + * e.g. + * - for top-left and a larger size: the bottom right corner should move down and out. + * - for top and smaller size: both the bottom corners should move in and up. + */ +export type Resizing = "top-left" | "top" | "top-right" | "right" | "bottom-right" | "bottom" | "bottom-left" | "left" | "center"; + +/** + * Identifies the type of the message to or from the iframe. + */ + /** * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an * iframe with the channel definitions and current channel selection. @@ -1819,7 +1919,7 @@ export interface IframeChannelsPayload { */ location?: Location; /** - * The id of the channel taht should be currently selected, or `null` if none should be + * The id of the channel that should be currently selected, or `null` if none should be * selected */ selected: null | string; @@ -1963,7 +2063,7 @@ export interface IframeMessage { /** * Identifies the type of the message to or from the iframe. */ -export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected" | "iframeChannelToggle" | "iframeChannelDrag"; +export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected" | "iframeChannelResize" | "iframeChannelDrag"; /** * Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an @@ -3491,6 +3591,22 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } + public static toIframeChannelDrag(json: string): IframeChannelDrag { + return cast(JSON.parse(json), r("IframeChannelDrag")); + } + + public static iframeChannelDragToJson(value: IframeChannelDrag): string { + return JSON.stringify(uncast(value, r("IframeChannelDrag")), null, 2); + } + + public static toIframeChannelResize(json: string): IframeChannelResize { + return cast(JSON.parse(json), r("IframeChannelResize")); + } + + public static iframeChannelResizeToJson(value: IframeChannelResize): string { + return JSON.stringify(uncast(value, r("IframeChannelResize")), null, 2); + } + public static toIframeChannels(json: string): IframeChannels { return cast(JSON.parse(json), r("IframeChannels")); } @@ -4297,6 +4413,29 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), + "IframeChannelDrag": o([ + { json: "payload", js: "payload", typ: r("IframeChannelDragPayload") }, + { json: "type", js: "type", typ: r("IframeChannelDragType") }, + ], false), + "IframeChannelDragPayload": o([ + { json: "mouse", js: "mouse", typ: r("MouseClass") }, + ], false), + "MouseClass": o([ + { json: "offsetX", js: "offsetX", typ: 0 }, + { json: "offsetY", js: "offsetY", typ: 0 }, + ], false), + "IframeChannelResize": o([ + { json: "payload", js: "payload", typ: r("IframeChannelResizePayload") }, + { json: "type", js: "type", typ: r("IframeChannelResizeType") }, + ], false), + "IframeChannelResizePayload": o([ + { json: "dimensions", js: "dimensions", typ: r("DimensionsClass") }, + { json: "resizeAnchor", js: "resizeAnchor", typ: r("Resizing") }, + ], false), + "DimensionsClass": o([ + { json: "height", js: "height", typ: 0 }, + { json: "width", js: "width", typ: 0 }, + ], false), "IframeChannels": o([ { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, { json: "type", js: "type", typ: r("IframeChannelsType") }, @@ -4820,6 +4959,23 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], + "IframeChannelDragType": [ + "iframeChannelDrag", + ], + "Resizing": [ + "bottom", + "bottom-left", + "bottom-right", + "center", + "left", + "right", + "top", + "top-left", + "top-right", + ], + "IframeChannelResizeType": [ + "iframeChannelResize", + ], "IframeChannelsType": [ "iframeChannels", ], @@ -4834,8 +4990,8 @@ const typeMap: any = { ], "IframeMessageType": [ "iframeChannelDrag", + "iframeChannelResize", "iframeChannelSelected", - "iframeChannelToggle", "iframeChannels", "iframeHandshake", "iframeHello", From 34147168e369baf67678fd34882d1a4bf88ebd84 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 19 Jun 2024 11:30:27 +0100 Subject: [PATCH 031/152] Correcting titles in WCP schemas and hence naming in generated types --- schemas/api/WCP1Hello.schema.json | 2 +- schemas/api/WCP2LoadUrl.schema.json | 2 +- schemas/api/WCP3Handshake.schema.json | 2 +- .../api/WCP4ValidateAppIdentity.schema.json | 2 +- ...idateAppIdentityFailedResponse.schema.json | 2 +- ...CP5ValidateAppIdentityResponse.schema.json | 2 +- schemas/api/api.schema.json | 2 +- src/api/BrowserTypes.ts | 187 +++++++++++------- 8 files changed, 125 insertions(+), 76 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 7c96ccdc7..f610710b1 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json", - "title": "Web Connection Protocol Hello", + "title": "Web Connection Protocol 1 Hello", "description": "Hello message sent by an application to a parent window or frame when attempting to establish connectivity to a Desktop Agent", "type": "object", "allOf": [ diff --git a/schemas/api/WCP2LoadUrl.schema.json b/schemas/api/WCP2LoadUrl.schema.json index a8e2d582b..420303faf 100644 --- a/schemas/api/WCP2LoadUrl.schema.json +++ b/schemas/api/WCP2LoadUrl.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json", - "title": "Web Connection Protocol LoadUrl", + "title": "Web Connection Protocol 2 Load Url", "description": "Response from a Desktop Agent to an application requesting access to it indicating that it should load a specified URL into a hidden iframe in order to establish connectivity to a Desktop Agent", "type": "object", "allOf": [ diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json index 6ce6c2dcc..07816b085 100644 --- a/schemas/api/WCP3Handshake.schema.json +++ b/schemas/api/WCP3Handshake.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json", - "title": "WCP3Handshake", + "title": "Web Connection Protocol 3 Handshake", "description": "Handshake message sent by the Desktop Agent to the app (with a MessagePort appended) that should be used for subsequent communication steps.", "type": "object", "allOf": [ diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json index 87bb0bd90..d9c94006d 100644 --- a/schemas/api/WCP4ValidateAppIdentity.schema.json +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json", - "title": "WCP4ValidateAppIdentity", + "title": "Web Connection Protocol 4 Validate App Identity", "description": "Identity Validation request from an app attempting to connect to a Desktop Agent.", "type": "object", "allOf": [ diff --git a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json index 5744b25a8..df1b85925 100644 --- a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json", - "title": "WCP5ValidateAppIdentityFailedResponse", + "title": "Web Connection Protocol 5 Validate App Identity Failed Response", "description": "Message sent by the Desktop Agent to an app if their identity validation fails.", "type": "object", "allOf": [ diff --git a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json index 697abb343..f2dd3f8cf 100644 --- a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json", - "title": "WCP5ValidateAppIdentityFailedResponse", + "title": "Web Connection Protocol 5 Validate App Identity Success Response", "description": "Message sent by the Desktop Agent to an app after successful identity validation", "type": "object", "allOf": [ diff --git a/schemas/api/api.schema.json b/schemas/api/api.schema.json index 49ea4c22b..b85153122 100644 --- a/schemas/api/api.schema.json +++ b/schemas/api/api.schema.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/api.schema.json", - "title": "FDC3 Desktop Agent API Schema", + "title": "FDC3 Desktop Agent API Schemas", "definitions": { "AppIdentifier": { "description": "Identifies an application, or instance of an application, and is used to target FDC3 API calls, such as `fdc3.open` or `fdc3.raiseIntent` at specific applications or application instances.\n\nWill always include at least an `appId` field, which uniquely identifies a specific app.\n\nIf the `instanceId` field is set then the `AppMetadata` object represents a specific instance of the application that may be addressed using that Id.", diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index b6e2bf88c..5da2e121d 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,8 +1,8 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocolHello, WebConnectionProtocolLoadURL, WCP3Handshake, WCP4ValidateAppIdentity, WCP5ValidateAppIdentityFailedResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // -// const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); +// const fDC3DesktopAgentAPISchemas = Convert.toFDC3DesktopAgentAPISchemas(json); // const commonDefinitions = Convert.toCommonDefinitions(json); // const appRequestMessage = Convert.toAppRequestMessage(json); // const agentResponseMessage = Convert.toAgentResponseMessage(json); @@ -69,11 +69,12 @@ // const raiseIntentRequest = Convert.toRaiseIntentRequest(json); // const raiseIntentResponse = Convert.toRaiseIntentResponse(json); // const raiseIntentResultResponse = Convert.toRaiseIntentResultResponse(json); -// const webConnectionProtocolHello = Convert.toWebConnectionProtocolHello(json); -// const webConnectionProtocolLoadURL = Convert.toWebConnectionProtocolLoadURL(json); -// const wCP3Handshake = Convert.toWCP3Handshake(json); -// const wCP4ValidateAppIdentity = Convert.toWCP4ValidateAppIdentity(json); -// const wCP5ValidateAppIdentityFailedResponse = Convert.toWCP5ValidateAppIdentityFailedResponse(json); +// const webConnectionProtocol1Hello = Convert.toWebConnectionProtocol1Hello(json); +// const webConnectionProtocol2LoadURL = Convert.toWebConnectionProtocol2LoadURL(json); +// const webConnectionProtocol3Handshake = Convert.toWebConnectionProtocol3Handshake(json); +// const webConnectionProtocol4ValidateAppIdentity = Convert.toWebConnectionProtocol4ValidateAppIdentity(json); +// const webConnectionProtocol5ValidateAppIdentityFailedResponse = Convert.toWebConnectionProtocol5ValidateAppIdentityFailedResponse(json); +// const webConnectionProtocol5ValidateAppIdentitySuccessResponse = Convert.toWebConnectionProtocol5ValidateAppIdentitySuccessResponse(json); // const webConnectionProtocolMessage = Convert.toWebConnectionProtocolMessage(json); // // These functions will throw an error if the JSON doesn't @@ -3066,12 +3067,12 @@ export interface IntentResult { * A message used during the connection flow for an application to a Desktop Agent in a * browser window. Used for messages sent in either direction. */ -export interface WebConnectionProtocolHello { +export interface WebConnectionProtocol1Hello { meta: ConnectionStepMetadata; /** * The message payload, containing data pertaining to this connection step. */ - payload: WebConnectionProtocolHelloPayload; + payload: WebConnectionProtocol1HelloPayload; /** * Identifies the type of the connection step message. */ @@ -3089,7 +3090,7 @@ export interface ConnectionStepMetadata { /** * The message payload, containing data pertaining to this connection step. */ -export interface WebConnectionProtocolHelloPayload { +export interface WebConnectionProtocol1HelloPayload { /** * A flag that may be used to indicate that a channel selector UI is or is not required. If * the app includes its own UI for displaying @@ -3119,12 +3120,12 @@ export interface WebConnectionProtocolHelloPayload { * A message used during the connection flow for an application to a Desktop Agent in a * browser window. Used for messages sent in either direction. */ -export interface WebConnectionProtocolLoadURL { +export interface WebConnectionProtocol2LoadURL { meta: ConnectionStepMetadata; /** * The message payload, containing data pertaining to this connection step. */ - payload: WebConnectionProtocolLoadURLPayload; + payload: WebConnectionProtocol2LoadURLPayload; /** * Identifies the type of the connection step message. */ @@ -3134,7 +3135,7 @@ export interface WebConnectionProtocolLoadURL { /** * The message payload, containing data pertaining to this connection step. */ -export interface WebConnectionProtocolLoadURLPayload { +export interface WebConnectionProtocol2LoadURLPayload { /** * A URL which can be used to establish communication with the Desktop Agent, via loading * the URL into an iframe and restarting the Web Connection protocol with the iframe as the @@ -3155,12 +3156,12 @@ export interface WebConnectionProtocolLoadURLPayload { * A message used during the connection flow for an application to a Desktop Agent in a * browser window. Used for messages sent in either direction. */ -export interface WCP3Handshake { +export interface WebConnectionProtocol3Handshake { meta: ConnectionStepMetadata; /** * The message payload, containing data pertaining to this connection step. */ - payload: WCP3HandshakePayload; + payload: WebConnectionProtocol3HandshakePayload; /** * Identifies the type of the connection step message. */ @@ -3170,7 +3171,7 @@ export interface WCP3Handshake { /** * The message payload, containing data pertaining to this connection step. */ -export interface WCP3HandshakePayload { +export interface WebConnectionProtocol3HandshakePayload { /** * Indicates whether a channel selector UI is required and the URL to use to do so. Set to * `true` to use the default or `false` to disable the channel selector (as the Desktop @@ -3199,12 +3200,12 @@ export interface WCP3HandshakePayload { * A message used during the connection flow for an application to a Desktop Agent in a * browser window. Used for messages sent in either direction. */ -export interface WCP4ValidateAppIdentity { +export interface WebConnectionProtocol4ValidateAppIdentity { meta: ConnectionStepMetadata; /** * The message payload, containing data pertaining to this connection step. */ - payload: WCP4ValidateAppIdentityPayload; + payload: WebConnectionProtocol4ValidateAppIdentityPayload; /** * Identifies the type of the connection step message. */ @@ -3214,7 +3215,7 @@ export interface WCP4ValidateAppIdentity { /** * The message payload, containing data pertaining to this connection step. */ -export interface WCP4ValidateAppIdentityPayload { +export interface WebConnectionProtocol4ValidateAppIdentityPayload { /** * URL for an App Directory record that provides identity details for the application * attempting to connect @@ -3237,6 +3238,35 @@ export interface WCP4ValidateAppIdentityPayload { instanceUuid?: string; } +/** + * Identifies the type of the connection step message. + */ + +/** + * Message sent by the Desktop Agent to an app if their identity validation fails. + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WebConnectionProtocol5ValidateAppIdentityFailedResponse { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload; + /** + * Identifies the type of the connection step message. + */ + type: "WCP5ValidateAppIdentityFailedResponse"; +} + +/** + * The message payload, containing data pertaining to this connection step. + */ +export interface WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload { + message?: string; +} + /** * Identifies the type of the connection step message. */ @@ -3247,12 +3277,12 @@ export interface WCP4ValidateAppIdentityPayload { * A message used during the connection flow for an application to a Desktop Agent in a * browser window. Used for messages sent in either direction. */ -export interface WCP5ValidateAppIdentityFailedResponse { +export interface WebConnectionProtocol5ValidateAppIdentitySuccessResponse { meta: ConnectionStepMetadata; /** * The message payload, containing data pertaining to this connection step. */ - payload: WCP5ValidateAppIdentityFailedResponsePayload; + payload: WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload; /** * Identifies the type of the connection step message. */ @@ -3262,7 +3292,7 @@ export interface WCP5ValidateAppIdentityFailedResponse { /** * The message payload, containing data pertaining to this connection step. */ -export interface WCP5ValidateAppIdentityFailedResponsePayload { +export interface WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload { /** * The appId that the app's identity was validated against */ @@ -3311,11 +3341,11 @@ export type ConnectionStepMessageType = "WCP1Hello" | "WCP2LoadUrl" | "WCP3Hands // Converts JSON strings to/from your types // and asserts the results of JSON.parse at runtime export class Convert { - public static toFDC3DesktopAgentAPISchema(json: string): any { + public static toFDC3DesktopAgentAPISchemas(json: string): any { return cast(JSON.parse(json), "any"); } - public static fDC3DesktopAgentAPISchemaToJson(value: any): string { + public static fDC3DesktopAgentAPISchemasToJson(value: any): string { return JSON.stringify(uncast(value, "any"), null, 2); } @@ -3847,44 +3877,52 @@ export class Convert { return JSON.stringify(uncast(value, r("RaiseIntentResultResponse")), null, 2); } - public static toWebConnectionProtocolHello(json: string): WebConnectionProtocolHello { - return cast(JSON.parse(json), r("WebConnectionProtocolHello")); + public static toWebConnectionProtocol1Hello(json: string): WebConnectionProtocol1Hello { + return cast(JSON.parse(json), r("WebConnectionProtocol1Hello")); + } + + public static webConnectionProtocol1HelloToJson(value: WebConnectionProtocol1Hello): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol1Hello")), null, 2); + } + + public static toWebConnectionProtocol2LoadURL(json: string): WebConnectionProtocol2LoadURL { + return cast(JSON.parse(json), r("WebConnectionProtocol2LoadURL")); } - public static webConnectionProtocolHelloToJson(value: WebConnectionProtocolHello): string { - return JSON.stringify(uncast(value, r("WebConnectionProtocolHello")), null, 2); + public static webConnectionProtocol2LoadURLToJson(value: WebConnectionProtocol2LoadURL): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol2LoadURL")), null, 2); } - public static toWebConnectionProtocolLoadURL(json: string): WebConnectionProtocolLoadURL { - return cast(JSON.parse(json), r("WebConnectionProtocolLoadURL")); + public static toWebConnectionProtocol3Handshake(json: string): WebConnectionProtocol3Handshake { + return cast(JSON.parse(json), r("WebConnectionProtocol3Handshake")); } - public static webConnectionProtocolLoadURLToJson(value: WebConnectionProtocolLoadURL): string { - return JSON.stringify(uncast(value, r("WebConnectionProtocolLoadURL")), null, 2); + public static webConnectionProtocol3HandshakeToJson(value: WebConnectionProtocol3Handshake): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol3Handshake")), null, 2); } - public static toWCP3Handshake(json: string): WCP3Handshake { - return cast(JSON.parse(json), r("WCP3Handshake")); + public static toWebConnectionProtocol4ValidateAppIdentity(json: string): WebConnectionProtocol4ValidateAppIdentity { + return cast(JSON.parse(json), r("WebConnectionProtocol4ValidateAppIdentity")); } - public static wCP3HandshakeToJson(value: WCP3Handshake): string { - return JSON.stringify(uncast(value, r("WCP3Handshake")), null, 2); + public static webConnectionProtocol4ValidateAppIdentityToJson(value: WebConnectionProtocol4ValidateAppIdentity): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol4ValidateAppIdentity")), null, 2); } - public static toWCP4ValidateAppIdentity(json: string): WCP4ValidateAppIdentity { - return cast(JSON.parse(json), r("WCP4ValidateAppIdentity")); + public static toWebConnectionProtocol5ValidateAppIdentityFailedResponse(json: string): WebConnectionProtocol5ValidateAppIdentityFailedResponse { + return cast(JSON.parse(json), r("WebConnectionProtocol5ValidateAppIdentityFailedResponse")); } - public static wCP4ValidateAppIdentityToJson(value: WCP4ValidateAppIdentity): string { - return JSON.stringify(uncast(value, r("WCP4ValidateAppIdentity")), null, 2); + public static webConnectionProtocol5ValidateAppIdentityFailedResponseToJson(value: WebConnectionProtocol5ValidateAppIdentityFailedResponse): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol5ValidateAppIdentityFailedResponse")), null, 2); } - public static toWCP5ValidateAppIdentityFailedResponse(json: string): WCP5ValidateAppIdentityFailedResponse { - return cast(JSON.parse(json), r("WCP5ValidateAppIdentityFailedResponse")); + public static toWebConnectionProtocol5ValidateAppIdentitySuccessResponse(json: string): WebConnectionProtocol5ValidateAppIdentitySuccessResponse { + return cast(JSON.parse(json), r("WebConnectionProtocol5ValidateAppIdentitySuccessResponse")); } - public static wCP5ValidateAppIdentityFailedResponseToJson(value: WCP5ValidateAppIdentityFailedResponse): string { - return JSON.stringify(uncast(value, r("WCP5ValidateAppIdentityFailedResponse")), null, 2); + public static webConnectionProtocol5ValidateAppIdentitySuccessResponseToJson(value: WebConnectionProtocol5ValidateAppIdentitySuccessResponse): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol5ValidateAppIdentitySuccessResponse")), null, 2); } public static toWebConnectionProtocolMessage(json: string): WebConnectionProtocolMessage { @@ -4691,55 +4729,63 @@ const typeMap: any = { { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, ], false), - "WebConnectionProtocolHello": o([ + "WebConnectionProtocol1Hello": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("WebConnectionProtocolHelloPayload") }, - { json: "type", js: "type", typ: r("WebConnectionProtocolHelloType") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol1HelloPayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol1HelloType") }, ], false), "ConnectionStepMetadata": o([ { json: "connectionAttemptUuid", js: "connectionAttemptUuid", typ: "" }, { json: "timestamp", js: "timestamp", typ: Date }, ], false), - "WebConnectionProtocolHelloPayload": o([ + "WebConnectionProtocol1HelloPayload": o([ { json: "channelSelector", js: "channelSelector", typ: u(undefined, true) }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, { json: "resolver", js: "resolver", typ: u(undefined, true) }, ], "any"), - "WebConnectionProtocolLoadURL": o([ + "WebConnectionProtocol2LoadURL": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("WebConnectionProtocolLoadURLPayload") }, - { json: "type", js: "type", typ: r("WebConnectionProtocolLoadURLType") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol2LoadURLPayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol2LoadURLType") }, ], false), - "WebConnectionProtocolLoadURLPayload": o([ + "WebConnectionProtocol2LoadURLPayload": o([ { json: "iframeUrl", js: "iframeUrl", typ: "" }, ], "any"), - "WCP3Handshake": o([ + "WebConnectionProtocol3Handshake": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("WCP3HandshakePayload") }, - { json: "type", js: "type", typ: r("WCP3HandshakeType") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol3HandshakePayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol3HandshakeType") }, ], false), - "WCP3HandshakePayload": o([ + "WebConnectionProtocol3HandshakePayload": o([ { json: "channelSelector", js: "channelSelector", typ: u(true, "") }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, { json: "resolver", js: "resolver", typ: u(true, "") }, ], false), - "WCP4ValidateAppIdentity": o([ + "WebConnectionProtocol4ValidateAppIdentity": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("WCP4ValidateAppIdentityPayload") }, - { json: "type", js: "type", typ: r("WCP4ValidateAppIdentityType") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol4ValidateAppIdentityPayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol4ValidateAppIdentityType") }, ], false), - "WCP4ValidateAppIdentityPayload": o([ + "WebConnectionProtocol4ValidateAppIdentityPayload": o([ { json: "appDUrl", js: "appDUrl", typ: u(undefined, "") }, { json: "appId", js: "appId", typ: "" }, { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, { json: "instanceUuid", js: "instanceUuid", typ: u(undefined, "") }, ], false), - "WCP5ValidateAppIdentityFailedResponse": o([ + "WebConnectionProtocol5ValidateAppIdentityFailedResponse": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol5ValidateAppIdentityFailedResponseType") }, + ], false), + "WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload": o([ + { json: "message", js: "message", typ: u(undefined, "") }, + ], false), + "WebConnectionProtocol5ValidateAppIdentitySuccessResponse": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: r("WCP5ValidateAppIdentityFailedResponsePayload") }, - { json: "type", js: "type", typ: r("WCP5ValidateAppIdentityFailedResponseType") }, + { json: "payload", js: "payload", typ: r("WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol5ValidateAppIdentitySuccessResponseType") }, ], false), - "WCP5ValidateAppIdentityFailedResponsePayload": o([ + "WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload": o([ { json: "appId", js: "appId", typ: "" }, { json: "implementationMetadata", js: "implementationMetadata", typ: r("ImplementationMetadata") }, { json: "instanceId", js: "instanceId", typ: "" }, @@ -5090,19 +5136,22 @@ const typeMap: any = { "RaiseIntentResultResponseType": [ "raiseIntentResultResponse", ], - "WebConnectionProtocolHelloType": [ + "WebConnectionProtocol1HelloType": [ "WCP1Hello", ], - "WebConnectionProtocolLoadURLType": [ + "WebConnectionProtocol2LoadURLType": [ "WCP2LoadUrl", ], - "WCP3HandshakeType": [ + "WebConnectionProtocol3HandshakeType": [ "WCP3Handshake", ], - "WCP4ValidateAppIdentityType": [ + "WebConnectionProtocol4ValidateAppIdentityType": [ "WCP4ValidateAppIdentity", ], - "WCP5ValidateAppIdentityFailedResponseType": [ + "WebConnectionProtocol5ValidateAppIdentityFailedResponseType": [ + "WCP5ValidateAppIdentityFailedResponse", + ], + "WebConnectionProtocol5ValidateAppIdentitySuccessResponseType": [ "WCP5ValidateAppIdentityResponse", ], "ConnectionStepMessageType": [ From 50fe214da5da19728fa246d2efcb12230b9d1f5b Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 27 Jun 2024 11:13:03 +0100 Subject: [PATCH 032/152] making originatingApp optional in intentEvent as its optional in the FDC3 API --- schemas/api/intentEvent.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/api/intentEvent.schema.json b/schemas/api/intentEvent.schema.json index b92d0ed30..a4d2b87cf 100644 --- a/schemas/api/intentEvent.schema.json +++ b/schemas/api/intentEvent.schema.json @@ -49,7 +49,7 @@ }, "additionalProperties": false, "required": [ - "intent", "context", "originatingApp" + "intent", "context" ] } } From 4e1f59e156764d1c15fc12547b32168c233280e9 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 1 Jul 2024 16:42:37 +0100 Subject: [PATCH 033/152] Regenerating types from schemas + migrating merged correction to findIntentsByContext request payload --- schemas/api/findIntentsByContextRequest.schema.json | 4 ++++ src/api/BrowserTypes.ts | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/schemas/api/findIntentsByContextRequest.schema.json b/schemas/api/findIntentsByContextRequest.schema.json index 5b43d0639..0b18240d2 100644 --- a/schemas/api/findIntentsByContextRequest.schema.json +++ b/schemas/api/findIntentsByContextRequest.schema.json @@ -33,6 +33,10 @@ "properties": { "context": { "$ref": "../context/context.schema.json" + }, + "resultType": { + "title": "Result type argument", + "type": "string" } }, "required": [ diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 5da2e121d..5f067387f 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1239,7 +1239,8 @@ export interface FindIntentsByContextRequest { * The message payload typically contains the arguments to FDC3 API functions. */ export interface FindIntentsByContextRequestPayload { - context: Context; + context: Context; + resultType?: string; } /** @@ -2177,7 +2178,7 @@ export interface IntentEventPayload { /** * Details of the application instance that raised the intent */ - originatingApp: AppIdentifier; + originatingApp?: AppIdentifier; } /** @@ -4329,6 +4330,7 @@ const typeMap: any = { ], false), "FindIntentsByContextRequestPayload": o([ { json: "context", js: "context", typ: r("Context") }, + { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), "FindIntentsByContextsByContextResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, @@ -4537,7 +4539,7 @@ const typeMap: any = { "IntentEventPayload": o([ { json: "context", js: "context", typ: r("Context") }, { json: "intent", js: "intent", typ: "" }, - { json: "originatingApp", js: "originatingApp", typ: r("AppIdentifier") }, + { json: "originatingApp", js: "originatingApp", typ: u(undefined, r("AppIdentifier")) }, ], false), "IntentListenerUnsubscribeRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, From b566ee12748a7b28d51a293c112dd14f75cc3a22 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 1 Jul 2024 18:26:55 +0100 Subject: [PATCH 034/152] Fixing extraneous closing tags in Errors.md --- docs/api/ref/Errors.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 14e96edd2..328eb09e8 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -442,9 +442,6 @@ public static class ResultError - - - ## `BridgingError` `@experimental` From 01b9284a4529c8c3fbdd8235a56c102ffcb636b1 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 4 Jul 2024 17:42:02 +0100 Subject: [PATCH 035/152] Navigation and requirements tweaks --- docs/api/spec.md | 17 +++++----- .../api/specs/browserCommunicationProtocol.md | 8 ++++- .../api/specs/browserResidentDesktopAgents.md | 33 ++++++++++++------- docs/api/specs/preloadDesktopAgents.md | 16 ++++++--- .../{getAgent.md => webConnectionProtocol.md} | 6 +++- docs/api/supported-platforms.md | 2 +- website/sidebars.json | 12 ++++++- 7 files changed, 66 insertions(+), 28 deletions(-) rename docs/api/specs/{getAgent.md => webConnectionProtocol.md} (98%) diff --git a/docs/api/spec.md b/docs/api/spec.md index 17d64cc15..40715a23b 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -90,12 +90,11 @@ There is currently no method of discovering all the apps supported by a Desktop An FDC3 Standard compliant Desktop Agent implementation **MUST**: -- Provide the FDC3 API to web applications via a global accessible as [`window.fdc3`](support-platforms#web). -- Provide a global [`fdc3Ready`](support-platforms#web) event to web applications that is fired when the API is ready for use. -- Provide a method of resolving ambiguous intents (i.e. those that might be resolved by multiple applications) or unspecified intents (calls to `raiseIntentForContext` that return multiple options), such as a resolver UI. - - Intent resolution MUST take into account any specified input or return context types - - Requests for resolution to apps returning a channel MUST include any apps that are registered as returning a channel with a specific type. -- Return (JavaScript or platform appropriate) Error Objects with messages from the [`ChannelError`](ref/Errors#channelerror), [`OpenError`](ref/Errors#openerror), [`ResolveError`](ref/Errors#resolveerror) and [`ResultError`](ref/Errors#resulterror) enumerations as appropriate. +- Provide the FDC3 API to web applications via one of the standardized interfaces for web applications: + - A global accessible as [`window.fdc3`](support-platforms#web) and a global [`fdc3Ready`](support-platforms#web) event that is fired when it has been made available. + - `window.postMessage` and the HTML CHannel Messaging API as defined in the [FDC3 Web Connection Protocol](webConnectionProtocol) and [FDC3 Browser Communication Protocol](specs/browserCommunicationProtocol). +- Implement the [Browser-Resident Desktop Agent spec](specs/browserResidentDesktopAgents.md) if it is intended to support apps running in a standard browser. +- Implement the [Preload Desktop Agent spec](specs/preloadDesktopAgents.md) if it is intended to support apps running in a container or other environment that supports injecting a global `fdc3` object. - Accept as input and return as output data structures that are compatible with the interfaces defined in this Standard. - Include implementations of the following [Desktop Agent](ref/DesktopAgent) API functions, as defined in this Standard: - [`addContextListener`](ref/DesktopAgent#addcontextlistener) @@ -113,12 +112,14 @@ An FDC3 Standard compliant Desktop Agent implementation **MUST**: - [`open`](ref/DesktopAgent#open) - [`raiseIntent`](ref/DesktopAgent#raiseintent) - [`raiseIntentForContext`](ref/DesktopAgent#raiseintentforcontext) +- Provide a method of resolving ambiguous intents (i.e. those that might be resolved by multiple applications) or unspecified intents (calls to `raiseIntentForContext` that return multiple options), such as a resolver UI. + - Intent resolution MUST take into account any specified input or return context types + - Requests for resolution to apps returning a channel MUST include any apps that are registered as returning a channel with a specific type. +- Return (JavaScript or platform appropriate) Error Objects with messages from the [`ChannelError`](ref/Errors#channelerror), [`OpenError`](ref/Errors#openerror), [`ResolveError`](ref/Errors#resolveerror) and [`ResultError`](ref/Errors#resulterror) enumerations as appropriate. - Provide an ID for each [`PrivateChannel`](ref/PrivateChannel) created via [`createPrivateChannel`](ref/DesktopAgent#createprivatechannel) and prevent them from being retrieved via [`getOrCreateChannel`](ref/DesktopAgent#getorcreatechannel) by ID. - Only require app directories that they connect to to have implemented only the minimum requirements specified in the [App Directory API Part](../app-directory/spec) of this Standard. - Provide details of whether they implement optional features of the Desktop Agent API in the `optionalFeatures` property of the [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object returned by the [`fdc3.getInfo()`](ref/DesktopAgent#getinfo) function. - Allow, by default, at least a 15 second timeout for an application, launched via [`fdc3.open`](../api/ref/DesktopAgent#open), [`fdc3.raiseIntent`](../api/ref/DesktopAgent#raiseintent) or [`fdc3.raiseIntentForContext`](../api/ref/DesktopAgent#raiseintentforcontext) to add any context listener (via [`fdc3.addContextListener`](../api/ref/DesktopAgent#addcontextlistener)) or intent listener (via [`fdc3.addIntentListener`](../api/ref/DesktopAgent#addintentlistener)) necessary to deliver context or intent and context to it on launch. This timeout only applies to listeners needed to receive context on launch; further intent and context listeners not required on launch MAY be added later. -- Implement the [Browser-Resident Desktop Agent spec](specs/browserResidentDesktopAgents.md) if it is intended to support apps running in a standard browser. -- Implement the [Preload Desktop Agent spec](specs/preloadDesktopAgents.md) if it is intended to support apps running in a container or other environment that supports injecting a global `fdc3` object. An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: diff --git a/docs/api/specs/browserCommunicationProtocol.md b/docs/api/specs/browserCommunicationProtocol.md index 09baa1cc2..052ca389e 100644 --- a/docs/api/specs/browserCommunicationProtocol.md +++ b/docs/api/specs/browserCommunicationProtocol.md @@ -1,3 +1,9 @@ +--- +id: browserCommunicationProtocol +sidebar_label: Browser Communication Protocol +title: Browser Communication Protocol (next) +--- + # Browser Communication Protocol (BCP) BCP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. @@ -28,7 +34,7 @@ When the API calls the unsubscriber for a listener then `BCPRemoveContextListene ## Intents -Refer [Private Channel examples](../api/ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. +Refer [Private Channel examples](../ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. When an app ("client") calls `raiseIntent()` or `raiseIntentByContext()`, the library MUST send the corresponding `BCPRaiseIntent` or `BCPRaiseIntentByContext` message to the DA. diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 7582af5dd..1174310e3 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -1,16 +1,30 @@ -# FDC for the Web: Browser-Resident Desktop Agent Specification +--- +id: browserDesktopAgents +sidebar_label: Browser Desktop Agents +title: Browser Desktop Agents (next) +--- This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by FINOS. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView or Browser Extension based implementations.) -> Note - Prior to FDC 3.0, only Preload DAs were supported. +This specification only applies to apps running in a browser and therefore assumes use of JavaScript and HTML APIs. Implementations in other languages such as .NET are not covered. Along with this specification, a new general connection strategy has been established for _all_ FDC3 compliant applications: FDC3 compliant apps SHOULD make use of the `@finos/fdc3` library to establish their FDC3 interface (a `DesktopAgent` object instance). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or Preload DAs without code modification. We refer to this concept as Write Once Run Anywhere (WORA). -> Note - Along with this specification, a new general connection strategy has been established for _all_ FDC3 compliant applications: FDC3 compliant apps SHOULD make use of the `@finos/fdc3` library to establish their FDC3 interface (a `DesktopAgent` object instance). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or Preload DAs without code modification. We refer to this concept as Write Once Run Anywhere (WORA). +:::info -> Note - This specification only applies to apps running in a browser and therefore assumes use of JavaScript and HTML APIs. Implementations in other languages such as .NET are not covered by this specification. +Prior to FDC3 2.2, only Preload DAs were supported. -This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the `@finos/fdc3` library. Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification](getAgent.md) for information on how the client side operates. +::: -> Note - When referencing "DA" in this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. +:::note + +This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the `@finos/fdc3` library. Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates. + +::: + +:::note + +When referencing "DA" in this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. + +::: ## Launching apps @@ -92,7 +106,7 @@ The first BCP message received should be a "WCPValidateAppIdentity" message. Wit > Note - Apps that call `window.open()` to create new instances of themselves can appear to be their Parent. This is because browsers may clone SessionStorage for newly opened windows. When a child window calls `getAgent()` with the same appId as the parent window, it will appear to the DA that a navigation event occurred on the parent window (because `instanceUuid` will be set, and will appear to match the appId). DAs therefore MUST track the WindowProxy object that is used to establish each connection and use it as an additional comparison criteria. Example BCP validation -```JavaScript +```js const processValidateAppIdentityBCP = (connection, e) => { const { data, source, origin } = e; @@ -217,7 +231,4 @@ A DA may implement its own Channel Selector and Intent Resolver or may utilize t DAs are responsible for tracking when app windows close by checking `win.closed` in a loop. -https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128 - - - +https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128 \ No newline at end of file diff --git a/docs/api/specs/preloadDesktopAgents.md b/docs/api/specs/preloadDesktopAgents.md index 5d66c5a26..4fec204bd 100644 --- a/docs/api/specs/preloadDesktopAgents.md +++ b/docs/api/specs/preloadDesktopAgents.md @@ -1,6 +1,11 @@ -# Preload DA Specification +--- +id: preloadDesktopAgents +sidebar_label: Preload Desktop Agents +title: Preload Desktop Agents (next) +--- -> Note - The [getAgent() specification](./getAgent.md) relies on Preload DAs behaving as specified in this document. FDC3 apps are now encouraged to use `getAgent()` instead of relying on the existence of the global object. + +> Note - The [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) relies on Preload DAs behaving as specified in this document. FDC3 apps are now encouraged to use `getAgent()` instead of relying on the existence of the global object. ## Injecting the global FDC3 object @@ -8,7 +13,7 @@ A Preload Desktop Agent MUST provide the FDC3 API via a global accessible as `wi The global `window.fdc3` must only be available after the API is ready to use. To enable applications to avoid using the API before it is ready, implementors MUST provide a global `fdc3Ready` event that is fired when the API is ready for use. Implementations should first check for the existence of the FDC3 API and add a listener for this event if it is not found: -```ts +```js function fdc3Action() { // Make some fdc3 API calls here } @@ -28,8 +33,9 @@ The global `window.fdc3` SHOULD only be available after the API is ready to use. In order to handle navigation events, Preload DAs SHOULD provide a `validateAppIdentity()` function on the global `fdc3` object (DesktopAgent interface). This function should behave the same as documented in the [Browser Resident DA Authentication Step](./browserResidentDesktopAgents.md#step-2---authentication), allowing the same combinations of appId and appDUrl as are allowed by `getAgent()`. -Example -```JavaScript +Example: + +```js const { instanceUuid, instanceId } = await fdc3.validateAppIdentity({ appId: "yourApp@yourOrg.com", instanceUuid: "some generated uuid" diff --git a/docs/api/specs/getAgent.md b/docs/api/specs/webConnectionProtocol.md similarity index 98% rename from docs/api/specs/getAgent.md rename to docs/api/specs/webConnectionProtocol.md index 946d21e53..0930b15e6 100644 --- a/docs/api/specs/getAgent.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -1,4 +1,8 @@ -# Specification for getAgent() function +--- +id: webConnectionProtocol +sidebar_label: Web Connection Protocol +title: FDC3 Web Connection Protocol (next) +--- `getAgent()` is implemented in the `@finos/fdc3` library. This specification details how it retrieves and provides FDC3 interface object. diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index b841eac19..38ddda9e5 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -16,7 +16,7 @@ There are two standardized types of interface to a DA that a web application may - **Preload**: Used where the desktop agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. - **Proxy**: Used when running in standard web browser (without an extension) and the desktop agent has to run in a different window or frame to the application and must be communicated with via Cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of Cross-document messaging, allowing the application to work with the FDC3 API directly. -The FDC3 Standard defines a Web Connection Protocol (WCP) that allows apps to work with either interface, by detecting which is applicable. The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. +The FDC3 Standard defines a [Web Connection Protocol (WCP)](specs/webConnectionProtocol) that allows apps to work with either interface, by detecting which is applicable. The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support either of the standardized interfaces. diff --git a/website/sidebars.json b/website/sidebars.json index 5306d6e7e..748aa0fe6 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -23,7 +23,17 @@ "api/ref/Types", "api/ref/Metadata", "api/ref/Errors", - "api/ref/Events" + "api/ref/Events", + { + "type": "category", + "label": "Desktop Agent Specs", + "items": [ + "api/specs/preloadDesktopAgents", + "api/specs/browserDesktopAgents", + "api/specs/webConnectionProtocol", + "api/specs/browserCommunicationProtocol" + ] + } ] }, { From fcc5bf4caa6ef62797c7e4b0980fccb79ef58c06 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 5 Jul 2024 11:26:40 +0100 Subject: [PATCH 036/152] Tidy up compliance requirements + a note in BCP --- docs/api/spec.md | 15 +++++++-------- docs/api/specs/browserCommunicationProtocol.md | 7 +++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 40715a23b..10d12e80c 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -67,9 +67,9 @@ The surface area of FDC3 standardization (shown in *white* above) itself is quit For example: -- workspace management -- user identity and SSO -- entitlements +- Workspace management +- User identity and SSO +- Entitlements - UX of application resolution Are all areas of functionality that any feature-complete desktop agent would implement, but are not currently areas considered for standardization under FDC3. @@ -90,11 +90,10 @@ There is currently no method of discovering all the apps supported by a Desktop An FDC3 Standard compliant Desktop Agent implementation **MUST**: -- Provide the FDC3 API to web applications via one of the standardized interfaces for web applications: - - A global accessible as [`window.fdc3`](support-platforms#web) and a global [`fdc3Ready`](support-platforms#web) event that is fired when it has been made available. - - `window.postMessage` and the HTML CHannel Messaging API as defined in the [FDC3 Web Connection Protocol](webConnectionProtocol) and [FDC3 Browser Communication Protocol](specs/browserCommunicationProtocol). -- Implement the [Browser-Resident Desktop Agent spec](specs/browserResidentDesktopAgents.md) if it is intended to support apps running in a standard browser. -- Implement the [Preload Desktop Agent spec](specs/preloadDesktopAgents.md) if it is intended to support apps running in a container or other environment that supports injecting a global `fdc3` object. +- Be able to provide the FDC3 API to applications in accordance with with any requirements defined for that platform, as defined in [Supported Platforms](supported-platforms) and linked specifications: + - For web applications this includes: + - Implementing the [Browser-Resident Desktop Agent spec](specs/browserResidentDesktopAgents.md) if it is intended to support apps running in a standard web browser. + - Implementing the [Preload Desktop Agent spec](specs/preloadDesktopAgents.md) if it is intended to support apps running in a container or other environment that supports injecting a global `fdc3` object. - Accept as input and return as output data structures that are compatible with the interfaces defined in this Standard. - Include implementations of the following [Desktop Agent](ref/DesktopAgent) API functions, as defined in this Standard: - [`addContextListener`](ref/DesktopAgent#addcontextlistener) diff --git a/docs/api/specs/browserCommunicationProtocol.md b/docs/api/specs/browserCommunicationProtocol.md index 052ca389e..0d828f62b 100644 --- a/docs/api/specs/browserCommunicationProtocol.md +++ b/docs/api/specs/browserCommunicationProtocol.md @@ -8,7 +8,11 @@ title: Browser Communication Protocol (next) BCP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. -> Note - We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. +:::note + +We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. + +::: Type definitions for all BCP messages can be found here: [bcp.ts](TODO). @@ -74,4 +78,3 @@ FDC3's `PrivateChannel` object has some specific functions, each of which has a The DA should send `BCPPrivateChannelOnAddContextListener` and `BCPPrivateChannelOnUnsubscribe` messages whenever `BCPAddContextListener` or `BCPRemoveContextListener` is called on a private channel. These will be delivered to the library regardless of whether a client has actually called `onAddContextListener()` and `onUnsubscribe()`. It is the library's responsibility to track these calls and either deliver or discard the messages accordingly. Likewise, the DA should send `BCPPrivateChannelOnDisconnect` whenever the `BCPPrivateChannelDisconnect` message is received. It is the library's responsibility to deliver or discard this message. - From d94171986ccca791d570439ecfc366e4f9665ca0 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 16 Jul 2024 12:26:51 +0300 Subject: [PATCH 037/152] add originating app metadata to broadcast event --- schemas/api/broadcastEvent.schema.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/schemas/api/broadcastEvent.schema.json b/schemas/api/broadcastEvent.schema.json index b6a28fe7e..3a8028255 100644 --- a/schemas/api/broadcastEvent.schema.json +++ b/schemas/api/broadcastEvent.schema.json @@ -40,6 +40,11 @@ "$ref": "../context/context.schema.json", "title": "Context", "description": "The context object that was broadcast." + }, + "originatingApp": { + "title": "Originating AppIdentifier", + "description": "Details of the application instance that broadcast the context", + "$ref": "api.schema.json#/definitions/AppIdentifier" } }, "additionalProperties": false, From 14952aed42b3ae8e214d146c45f4bd123baa7052 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 23 Jul 2024 12:41:14 +0100 Subject: [PATCH 038/152] Fix OpenRequest context omission and FIndIntentsByContextResponse typo --- package.json | 4 +- .../findIntentsByContextResponse.schema.json | 2 +- schemas/api/openRequest.schema.json | 5 ++ src/api/BrowserTypes.ts | 46 +++++++++++++------ 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 0194a400c..cc4ea6158 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,14 @@ "scripts": { "clean": "rimraf dist", "start": "rollup -c rollup.config.js --bundleConfigAsCjs -w", - "prebuild": "npm run clean && npm run typegen && npm run typegen-bridging && npm run lint", + "prebuild": "npm run clean && npm run typegen && npm run typegen-browser && npm run typegen-bridging && npm run lint", "build": "rollup -c rollup.config.js --silent --bundleConfigAsCjs", "test": "jest --verbose", "lint": "eslint src/ test/ --ext .ts --fix", "prepack": "npm run build", "typegen": "node s2tQuicktypeUtil.js schemas/context src/context/ContextTypes.ts", "typegen-browser": "node s2tQuicktypeUtil.js schemas/api/api.schema.json schemas/api/common.schema.json schemas/api/appRequest.schema.json schemas/api/agentResponse.schema.json schemas/api/agentEvent.schema.json schemas/api src/api/BrowserTypes.ts", - "typegen-bridging": "node s2tQuicktypeUtil.js schemas/api schemas/bridging schemas/context/context.schema.json src/bridging/BridgingTypes.ts" + "typegen-bridging": "node s2tQuicktypeUtil.js schemas/api/api.schema.json schemas/api/common.schema.json schemas/bridging schemas/context/context.schema.json src/bridging/BridgingTypes.ts" }, "husky": { "hooks": { diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index 7ce0819cd..2957b7bf7 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json", "type": "object", - "title": "FindIntentsByContextsByContext Response", + "title": "FindIntentsByContexts Response", "description": "A response to a findIntentsByContext request.", "allOf": [ { diff --git a/schemas/api/openRequest.schema.json b/schemas/api/openRequest.schema.json index 9a50758bf..042e786ae 100644 --- a/schemas/api/openRequest.schema.json +++ b/schemas/api/openRequest.schema.json @@ -33,6 +33,11 @@ "properties": { "app": { "$ref": "api.schema.json#/definitions/AppIdentifier" + }, + "context": { + "$ref": "../context/context.schema.json", + "title": "Context", + "description": "If a Context object is passed in, this object will be provided to the opened application via a contextListener. The Context argument is functionally equivalent to opening the target app with no context and broadcasting the context directly to it." } }, "required": [ diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 5f067387f..644065548 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // // const fDC3DesktopAgentAPISchemas = Convert.toFDC3DesktopAgentAPISchemas(json); // const commonDefinitions = Convert.toCommonDefinitions(json); @@ -24,7 +24,7 @@ // const findIntentRequest = Convert.toFindIntentRequest(json); // const findIntentResponse = Convert.toFindIntentResponse(json); // const findIntentsByContextRequest = Convert.toFindIntentsByContextRequest(json); -// const findIntentsByContextsByContextResponse = Convert.toFindIntentsByContextsByContextResponse(json); +// const findIntentsByContextsResponse = Convert.toFindIntentsByContextsResponse(json); // const getAppMetadataRequest = Convert.toGetAppMetadataRequest(json); // const getAppMetadataResponse = Convert.toGetAppMetadataResponse(json); // const getCurrentChannelRequest = Convert.toGetCurrentChannelRequest(json); @@ -132,6 +132,8 @@ export interface AppRequestMessageMeta { * Field that represents the source application that the request being responded to was * received from, for debugging purposes. * + * Details of the application instance that broadcast the context + * * The App resolution option chosen * * Details of the application instance that raised the intent @@ -509,6 +511,10 @@ export interface BroadcastEventPayload { * The context object that was broadcast. */ context: Context; + /** + * Details of the application instance that broadcast the context + */ + originatingApp?: AppIdentifier; } /** @@ -518,6 +524,10 @@ export interface BroadcastEventPayload { * * The context object passed with the raised intent. * + * If a Context object is passed in, this object will be provided to the opened application + * via a contextListener. The Context argument is functionally equivalent to opening the + * target app with no context and broadcasting the context directly to it. + * * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by * FDC3 operations. As such, it is not really meant to be used on its own, but is imported * by more specific type definitions (standardized or custom) to provide the structure and @@ -1254,7 +1264,7 @@ export interface FindIntentsByContextRequestPayload { * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface FindIntentsByContextsByContextResponse { +export interface FindIntentsByContextsResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1264,7 +1274,7 @@ export interface FindIntentsByContextsByContextResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: FindIntentsByContextsByContextResponsePayload; + payload: FindIntentsByContextsResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -1277,7 +1287,7 @@ export interface FindIntentsByContextsByContextResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface FindIntentsByContextsByContextResponsePayload { +export interface FindIntentsByContextsResponsePayload { error?: FindInstancesErrors; appIntents?: AppIntent[]; } @@ -2418,6 +2428,12 @@ export interface OpenRequest { */ export interface OpenRequestPayload { app: AppIdentifier; + /** + * If a Context object is passed in, this object will be provided to the opened application + * via a contextListener. The Context argument is functionally equivalent to opening the + * target app with no context and broadcasting the context directly to it. + */ + context?: Context; } /** @@ -3518,12 +3534,12 @@ export class Convert { return JSON.stringify(uncast(value, r("FindIntentsByContextRequest")), null, 2); } - public static toFindIntentsByContextsByContextResponse(json: string): FindIntentsByContextsByContextResponse { - return cast(JSON.parse(json), r("FindIntentsByContextsByContextResponse")); + public static toFindIntentsByContextsResponse(json: string): FindIntentsByContextsResponse { + return cast(JSON.parse(json), r("FindIntentsByContextsResponse")); } - public static findIntentsByContextsByContextResponseToJson(value: FindIntentsByContextsByContextResponse): string { - return JSON.stringify(uncast(value, r("FindIntentsByContextsByContextResponse")), null, 2); + public static findIntentsByContextsResponseToJson(value: FindIntentsByContextsResponse): string { + return JSON.stringify(uncast(value, r("FindIntentsByContextsResponse")), null, 2); } public static toGetAppMetadataRequest(json: string): GetAppMetadataRequest { @@ -4184,6 +4200,7 @@ const typeMap: any = { "BroadcastEventPayload": o([ { json: "channelId", js: "channelId", typ: "" }, { json: "context", js: "context", typ: r("Context") }, + { json: "originatingApp", js: "originatingApp", typ: u(undefined, r("AppIdentifier")) }, ], false), "Context": o([ { json: "id", js: "id", typ: u(undefined, m("any")) }, @@ -4332,12 +4349,12 @@ const typeMap: any = { { json: "context", js: "context", typ: r("Context") }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), - "FindIntentsByContextsByContextResponse": o([ + "FindIntentsByContextsResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, - { json: "payload", js: "payload", typ: r("FindIntentsByContextsByContextResponsePayload") }, - { json: "type", js: "type", typ: r("FindIntentsByContextsByContextResponseType") }, + { json: "payload", js: "payload", typ: r("FindIntentsByContextsResponsePayload") }, + { json: "type", js: "type", typ: r("FindIntentsByContextsResponseType") }, ], false), - "FindIntentsByContextsByContextResponsePayload": o([ + "FindIntentsByContextsResponsePayload": o([ { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, { json: "appIntents", js: "appIntents", typ: u(undefined, a(r("AppIntent"))) }, ], false), @@ -4592,6 +4609,7 @@ const typeMap: any = { ], false), "OpenRequestPayload": o([ { json: "app", js: "app", typ: r("AppIdentifier") }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, ], false), "OpenResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, @@ -4968,7 +4986,7 @@ const typeMap: any = { "FindIntentsByContextRequestType": [ "findIntentsByContextRequest", ], - "FindIntentsByContextsByContextResponseType": [ + "FindIntentsByContextsResponseType": [ "findIntentsByContextResponse", ], "GetAppMetadataRequestType": [ From 9ac3ab462393fdbbee9c3e805410621436eee9e9 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 23 Jul 2024 14:24:10 +0100 Subject: [PATCH 039/152] Fix to typescript generation from schema scripts --- package.json | 6 +- s2tQuicktypeUtil.js | 23 +- src/bridging/BridgingTypes.ts | 466 +++++++++++++++++----------------- 3 files changed, 248 insertions(+), 247 deletions(-) diff --git a/package.json b/package.json index cc4ea6158..c94bf5a7d 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "test": "jest --verbose", "lint": "eslint src/ test/ --ext .ts --fix", "prepack": "npm run build", - "typegen": "node s2tQuicktypeUtil.js schemas/context src/context/ContextTypes.ts", - "typegen-browser": "node s2tQuicktypeUtil.js schemas/api/api.schema.json schemas/api/common.schema.json schemas/api/appRequest.schema.json schemas/api/agentResponse.schema.json schemas/api/agentEvent.schema.json schemas/api src/api/BrowserTypes.ts", - "typegen-bridging": "node s2tQuicktypeUtil.js schemas/api/api.schema.json schemas/api/common.schema.json schemas/bridging schemas/context/context.schema.json src/bridging/BridgingTypes.ts" + "typegen": "cd schemas && node ../s2tQuicktypeUtil.js context ../src/context/ContextTypes.ts", + "typegen-browser": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json context/context.schema.json api ../src/api/BrowserTypes.ts", + "typegen-bridging": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json api/broadcastRequest.schema.json api/findInstancesRequest.schema.json api/findInstancesResponse.schema.json api/findIntentRequest.schema.json api/findIntentResponse.schema.json api/findIntentsByContextRequest.schema.json api/findIntentsByContextResponse.schema.json api/getAppMetadataRequest.schema.json api/getAppMetadataResponse.schema.json api/openRequest.schema.json api/openResponse.schema.json api/raiseIntentRequest.schema.json api/raiseIntentResponse.schema.json api/raiseIntentResultResponse.schema.json context/context.schema.json api/ bridging ../src/bridging/BridgingTypes.ts" }, "husky": { "hooks": { diff --git a/s2tQuicktypeUtil.js b/s2tQuicktypeUtil.js index 3feaac711..4195a7f09 100644 --- a/s2tQuicktypeUtil.js +++ b/s2tQuicktypeUtil.js @@ -7,7 +7,11 @@ * quicktype bug in command line argument handling (where a directory is used * as input the source language argument is ignored which causes our schemas * to be interpreted as JSON input, rather than JSONSchema). - * Bug issue: + * + * Individual file arguments will be interpreted as 'additional' schema files + * that will be referenced from the other schemas and may not have top-level output + * schemas generated, while folders of files w + * * */ const path = require('path'); @@ -18,19 +22,19 @@ const args = process.argv.slice(2); const outputFile = args.pop(); const inputs = args; -console.log('Inputs: ' + inputs.join(' | ')); +console.log('Inputs schema files: ' + inputs.join(' | ')); console.log('Output file argument: ' + outputFile); let sources = ''; - +let additionalSchemaFiles = '' let dirIndex = 0; //skip duplicate paths (we might want to specify some files to go first, and might duplicate them) const paths = new Set(); -const addAPath = (aPath,paths,sources) => { +const addAPath = (aPath,paths,sources,type) => { if (!paths.has(aPath)) { paths.add(aPath) - return sources + ` --src ${aPath}`; + return sources + ` ${type} ${aPath}`; } else { console.log(`skipping duplicate path ${aPath}`); return sources; @@ -39,13 +43,14 @@ const addAPath = (aPath,paths,sources) => { while (dirIndex < inputs.length) { if (inputs[dirIndex].endsWith('.schema.json')) { - sources = addAPath(path.join(inputs[dirIndex]),paths,sources); + //add individual files with -S as additional schema files used in references (rather than ones that need a top-level output) + sources = addAPath(path.join(inputs[dirIndex]),paths,additionalSchemaFiles, "-S"); } else { fs.readdirSync(inputs[dirIndex], { withFileTypes: true }).forEach(file => { if (file.isDirectory()) { inputs.push(path.join(inputs[dirIndex], file.name)); } else if (file.name.endsWith('.schema.json')) { - sources = addAPath(path.join(inputs[dirIndex], file.name),paths,sources); + sources = addAPath(path.join(inputs[dirIndex], file.name),paths,sources, "--src"); } }); } @@ -54,9 +59,9 @@ while (dirIndex < inputs.length) { // Normalise path to local quicktype executable. //const quicktypeExec = "node " + ["..","quicktype","dist","index.js"].join(path.sep); -const quicktypeExec = ['.', 'node_modules', '.bin', 'quicktype'].join(path.sep); +const quicktypeExec = ['..', 'node_modules', '.bin', 'quicktype'].join(path.sep); -const command = `${quicktypeExec} --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${sources}`; +const command = `${quicktypeExec} --prefer-const-values --prefer-unions -s schema -o ${outputFile} ${additionalSchemaFiles} ${sources}`; console.log('command to run: ' + command); exec(command, function(error, stdout, stderr) { diff --git a/src/bridging/BridgingTypes.ts b/src/bridging/BridgingTypes.ts index 23d4c6faa..b558565bf 100644 --- a/src/bridging/BridgingTypes.ts +++ b/src/bridging/BridgingTypes.ts @@ -1,8 +1,7 @@ // To parse this data: // -// import { Convert, AgentErrorResponseMessage, AgentRequestMessage, AgentResponseMessage, BridgeErrorResponseMessage, BridgeRequestMessage, BridgeResponseMessage, BroadcastAgentRequest, BroadcastBridgeRequest, ConnectionStepMessage, ConnectionStep2Hello, ConnectionStep3Handshake, ConnectionStep4AuthenticationFailed, ConnectionStep6ConnectedAgentsUpdate, FindInstancesAgentErrorResponse, FindInstancesAgentRequest, FindInstancesAgentResponse, FindInstancesBridgeErrorResponse, FindInstancesBridgeRequest, FindInstancesBridgeResponse, FindIntentAgentErrorResponse, FindIntentAgentRequest, FindIntentAgentResponse, FindIntentBridgeErrorResponse, FindIntentBridgeRequest, FindIntentBridgeResponse, FindIntentsByContextAgentErrorResponse, FindIntentsByContextAgentRequest, FindIntentsByContextAgentResponse, FindIntentsByContextBridgeErrorResponse, FindIntentsByContextBridgeRequest, FindIntentsByContextBridgeResponse, GetAppMetadataAgentErrorResponse, GetAppMetadataAgentRequest, GetAppMetadataAgentResponse, GetAppMetadataBridgeErrorResponse, GetAppMetadataBridgeRequest, GetAppMetadataBridgeResponse, OpenAgentErrorResponse, OpenAgentRequest, OpenAgentResponse, OpenBridgeErrorResponse, OpenBridgeRequest, OpenBridgeResponse, PrivateChannelBroadcastAgentRequest, PrivateChannelBroadcastBridgeRequest, PrivateChannelEventListenerAddedAgentRequest, PrivateChannelEventListenerAddedBridgeRequest, PrivateChannelEventListenerRemovedAgentRequest, PrivateChannelEventListenerRemovedBridgeRequest, PrivateChannelOnAddContextListenerAgentRequest, PrivateChannelOnAddContextListenerBridgeRequest, PrivateChannelOnDisconnectAgentRequest, PrivateChannelOnDisconnectBridgeRequest, PrivateChannelOnUnsubscribeAgentRequest, PrivateChannelOnUnsubscribeBridgeRequest, RaiseIntentAgentErrorResponse, RaiseIntentAgentRequest, RaiseIntentAgentResponse, RaiseIntentBridgeErrorResponse, RaiseIntentBridgeRequest, RaiseIntentBridgeResponse, RaiseIntentResultAgentErrorResponse, RaiseIntentResultAgentResponse, RaiseIntentResultBridgeErrorResponse, RaiseIntentResultBridgeResponse, Context } from "./file"; +// import { Convert, AgentErrorResponseMessage, AgentRequestMessage, AgentResponseMessage, BridgeErrorResponseMessage, BridgeRequestMessage, BridgeResponseMessage, BroadcastAgentRequest, BroadcastBridgeRequest, ConnectionStepMessage, ConnectionStep2Hello, ConnectionStep3Handshake, ConnectionStep4AuthenticationFailed, ConnectionStep6ConnectedAgentsUpdate, FindInstancesAgentErrorResponse, FindInstancesAgentRequest, FindInstancesAgentResponse, FindInstancesBridgeErrorResponse, FindInstancesBridgeRequest, FindInstancesBridgeResponse, FindIntentAgentErrorResponse, FindIntentAgentRequest, FindIntentAgentResponse, FindIntentBridgeErrorResponse, FindIntentBridgeRequest, FindIntentBridgeResponse, FindIntentsByContextAgentErrorResponse, FindIntentsByContextAgentRequest, FindIntentsByContextAgentResponse, FindIntentsByContextBridgeErrorResponse, FindIntentsByContextBridgeRequest, FindIntentsByContextBridgeResponse, GetAppMetadataAgentErrorResponse, GetAppMetadataAgentRequest, GetAppMetadataAgentResponse, GetAppMetadataBridgeErrorResponse, GetAppMetadataBridgeRequest, GetAppMetadataBridgeResponse, OpenAgentErrorResponse, OpenAgentRequest, OpenAgentResponse, OpenBridgeErrorResponse, OpenBridgeRequest, OpenBridgeResponse, PrivateChannelBroadcastAgentRequest, PrivateChannelBroadcastBridgeRequest, PrivateChannelEventListenerAddedAgentRequest, PrivateChannelEventListenerAddedBridgeRequest, PrivateChannelEventListenerRemovedAgentRequest, PrivateChannelEventListenerRemovedBridgeRequest, PrivateChannelOnAddContextListenerAgentRequest, PrivateChannelOnAddContextListenerBridgeRequest, PrivateChannelOnDisconnectAgentRequest, PrivateChannelOnDisconnectBridgeRequest, PrivateChannelOnUnsubscribeAgentRequest, PrivateChannelOnUnsubscribeBridgeRequest, RaiseIntentAgentErrorResponse, RaiseIntentAgentRequest, RaiseIntentAgentResponse, RaiseIntentBridgeErrorResponse, RaiseIntentBridgeRequest, RaiseIntentBridgeResponse, RaiseIntentResultAgentErrorResponse, RaiseIntentResultAgentResponse, RaiseIntentResultBridgeErrorResponse, RaiseIntentResultBridgeResponse } from "./file"; // -// const fDC3DesktopAgentAPISchema = Convert.toFDC3DesktopAgentAPISchema(json); // const agentErrorResponseMessage = Convert.toAgentErrorResponseMessage(json); // const agentRequestMessage = Convert.toAgentRequestMessage(json); // const agentResponseMessage = Convert.toAgentResponseMessage(json); @@ -11,7 +10,7 @@ // const bridgeResponseMessage = Convert.toBridgeResponseMessage(json); // const broadcastAgentRequest = Convert.toBroadcastAgentRequest(json); // const broadcastBridgeRequest = Convert.toBroadcastBridgeRequest(json); -// const bridgingCommons = Convert.toBridgingCommons(json); +// const bridgeCommonDefinitions = Convert.toBridgeCommonDefinitions(json); // const connectionStepMessage = Convert.toConnectionStepMessage(json); // const connectionStep2Hello = Convert.toConnectionStep2Hello(json); // const connectionStep3Handshake = Convert.toConnectionStep3Handshake(json); @@ -69,7 +68,6 @@ // const raiseIntentResultAgentResponse = Convert.toRaiseIntentResultAgentResponse(json); // const raiseIntentResultBridgeErrorResponse = Convert.toRaiseIntentResultBridgeErrorResponse(json); // const raiseIntentResultBridgeResponse = Convert.toRaiseIntentResultBridgeResponse(json); -// const context = Convert.toContext(json); // // These functions will throw an error if the JSON doesn't // match the expected interface, even if the JSON is valid. @@ -549,12 +547,14 @@ export interface BroadcastAgentRequestPayload { */ channelId: string; /** - * The context object that was the payload of a broadcast message. + * The context object that is to be broadcast. */ - context: ContextElement; + context: Context; } /** + * The context object that is to be broadcast. + * * The context object that was the payload of a broadcast message. * * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by @@ -569,7 +569,7 @@ export interface BroadcastAgentRequestPayload { * data object of a particular type can be expected to have, but this can always be extended * with custom fields as appropriate. */ -export interface ContextElement { +export interface Context { /** * Context data objects may include a set of equivalent key-value pairs that can be used to * help applications identify and look up the context type they receive in their own domain. @@ -614,9 +614,10 @@ export interface ContextElement { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -730,9 +731,9 @@ export interface BroadcastBridgeRequestPayload { */ channelId: string; /** - * The context object that was the payload of a broadcast message. + * The context object that is to be broadcast. */ - context: ContextElement; + context: Context; } /** @@ -855,7 +856,7 @@ export interface ConnectionStep3HandshakePayload { * channels), as a mapping of channel id to an array of Context objects, one per type found * in the channel, most recent first. */ - channelsState: { [key: string]: ContextElement[] }; + channelsState: { [key: string]: Context[] }; /** * Desktop Agent ImplementationMetadata trying to connect to the bridge. */ @@ -1003,7 +1004,7 @@ export interface ConnectionStep6ConnectedAgentsUpdatePayload { * The updated state of channels that should be adopted by the agents. Should only be set * when an agent is connecting to the bridge. */ - channelsState?: { [key: string]: ContextElement[] }; + channelsState?: { [key: string]: Context[] }; /** * Should be set when an agent disconnects from the bridge and provide the name that no * longer is assigned. @@ -1058,7 +1059,7 @@ export interface FindInstancesAgentErrorResponse { /** * Error message payload containing an standardized error string. */ - payload: FindInstancesAgentErrorResponsePayload; + payload: PayloadClass; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -1078,11 +1079,16 @@ export interface FindInstancesAgentErrorResponseMeta { /** * Error message payload containing an standardized error string. */ -export interface FindInstancesAgentErrorResponsePayload { - error: ErrorMessage; +export interface PayloadClass { + error: FindInstancesErrors; } /** + * Unique identifier for a request or event message. Required in all message types + * + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. + * * Constants representing the errors that can be encountered when calling the `findIntent`, * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). @@ -1094,15 +1100,16 @@ export interface FindInstancesAgentErrorResponsePayload { * Constants representing the errors that can be encountered when calling the `open` method * on the DesktopAgent object (`fdc3`). */ -export type ErrorMessage = "DesktopAgentNotFound" | "IntentDeliveryFailed" | "MalformedContext" | "NoAppsFound" | "ResolverTimeout" | "ResolverUnavailable" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; +export type FindInstancesErrors = "DesktopAgentNotFound" | "IntentDeliveryFailed" | "MalformedContext" | "NoAppsFound" | "ResolverTimeout" | "ResolverUnavailable" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -1259,9 +1266,10 @@ export interface AppIdentifier { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -1270,7 +1278,7 @@ export interface AppIdentifier { * A response message from a Desktop Agent to the Bridge. */ export interface FindInstancesAgentResponse { - meta: FindInstancesAgentResponseMeta; + meta: AgentResponseMetadata; /** * The message payload typically contains return values for FDC3 API functions. */ @@ -1283,15 +1291,10 @@ export interface FindInstancesAgentResponse { } /** - * Metadata for a response messages sent by a Desktop Agent to the Bridge - */ -export interface FindInstancesAgentResponseMeta { - requestUuid: string; - responseUuid: string; - timestamp: Date; -} - -/** + * The message payload contains a flag indicating whether the API call was successful, plus + * any return values for the FDC3 API function called, or indicating that the request + * resulted in an error and including a standardized error message. + * * The message payload typically contains return values for FDC3 API functions. */ export interface FindInstancesAgentResponsePayload { @@ -1427,7 +1430,7 @@ export interface FindInstancesBridgeErrorResponse { * The error message payload contains details of an error return to the app or agent that * raised the original request. */ - payload: FindInstancesBridgeErrorResponsePayload; + payload: MessagePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -1450,8 +1453,8 @@ export interface FindInstancesBridgeErrorResponseMeta { * The error message payload contains details of an error return to the app or agent that * raised the original request. */ -export interface FindInstancesBridgeErrorResponsePayload { - error: ErrorMessage; +export interface MessagePayload { + error: FindInstancesErrors; } /** @@ -1582,7 +1585,7 @@ export interface FindInstancesBridgeRequestPayload { * request. */ export interface FindInstancesBridgeResponse { - meta: FindInstancesBridgeResponseMeta; + meta: BridgeResponseMessageMeta; /** * The message payload typically contains return values for FDC3 API functions. */ @@ -1595,18 +1598,10 @@ export interface FindInstancesBridgeResponse { } /** - * Metadata required in a response message collated and/or forwarded on by the Bridge - */ -export interface FindInstancesBridgeResponseMeta { - errorDetails?: ResponseErrorDetail[]; - errorSources?: DesktopAgentIdentifier[]; - requestUuid: string; - responseUuid: string; - sources?: DesktopAgentIdentifier[]; - timestamp: Date; -} - -/** + * The message payload contains a flag indicating whether the API call was successful, plus + * any return values for the FDC3 API function called, or indicating that the request + * resulted in an error and including a standardized error message. + * * The message payload typically contains return values for FDC3 API functions. */ export interface FindInstancesBridgeResponsePayload { @@ -1645,16 +1640,17 @@ export interface FindIntentAgentErrorResponseMeta { * Error message payload containing an standardized error string. */ export interface FindIntentAgentErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -1698,7 +1694,7 @@ export interface FindIntentAgentRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface FindIntentAgentRequestPayload { - context?: ContextElement; + context?: Context; intent: string; resultType?: string; } @@ -1707,9 +1703,10 @@ export interface FindIntentAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -1812,7 +1809,7 @@ export interface FindIntentBridgeErrorResponseMeta { * raised the original request. */ export interface FindIntentBridgeErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** @@ -1857,7 +1854,7 @@ export interface FindIntentBridgeRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface FindIntentBridgeRequestPayload { - context?: ContextElement; + context?: Context; intent: string; resultType?: string; } @@ -1932,16 +1929,17 @@ export interface FindIntentsByContextAgentErrorResponseMeta { * Error message payload containing an standardized error string. */ export interface FindIntentsByContextAgentErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -1986,7 +1984,7 @@ export interface FindIntentsByContextAgentRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface FindIntentsByContextAgentRequestPayload { - context: ContextElement; + context: Context; resultType?: string; } @@ -1994,9 +1992,10 @@ export interface FindIntentsByContextAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2069,7 +2068,7 @@ export interface FindIntentsByContextBridgeErrorResponseMeta { * raised the original request. */ export interface FindIntentsByContextBridgeErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** @@ -2115,7 +2114,7 @@ export interface FindIntentsByContextBridgeRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface FindIntentsByContextBridgeRequestPayload { - context: ContextElement; + context: Context; resultType?: string; } @@ -2189,16 +2188,17 @@ export interface GetAppMetadataAgentErrorResponseMeta { * Error message payload containing an standardized error string. */ export interface GetAppMetadataAgentErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2242,7 +2242,7 @@ export interface GetAppMetadataAgentRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface GetAppMetadataAgentRequestPayload { - app: AppDestinationIdentifier; + app: AppObject; } /** @@ -2282,7 +2282,7 @@ export interface GetAppMetadataAgentRequestPayload { * `source.instanceId` MUST be set, indicating the specific app instance that * received the intent. */ -export interface AppDestinationIdentifier { +export interface AppObject { /** * Used in Desktop Agent Bridging to attribute or target a message to a * particular Desktop Agent. @@ -2308,9 +2308,10 @@ export interface AppDestinationIdentifier { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2383,7 +2384,7 @@ export interface GetAppMetadataBridgeErrorResponseMeta { * raised the original request. */ export interface GetAppMetadataBridgeErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** @@ -2428,7 +2429,7 @@ export interface GetAppMetadataBridgeRequestMeta { * The message payload typically contains the arguments to FDC3 API functions. */ export interface GetAppMetadataBridgeRequestPayload { - app: AppDestinationIdentifier; + app: AppObject; } /** @@ -2501,7 +2502,7 @@ export interface OpenAgentErrorResponseMeta { * Error message payload containing an standardized error string. */ export interface OpenAgentErrorResponsePayload { - error: OpenErrorMessage; + error: OpenErrorResponsePayload; } /** @@ -2516,15 +2517,16 @@ export interface OpenAgentErrorResponsePayload { * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). */ -export type OpenErrorMessage = "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "MalformedContext" | "ResolverUnavailable" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; +export type OpenErrorResponsePayload = "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "MalformedContext" | "ResolverUnavailable" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2572,7 +2574,7 @@ export interface OpenAgentRequestPayload { * The application to open on the specified Desktop Agent */ app: AppToOpen; - context?: ContextElement; + context?: Context; } /** @@ -2637,9 +2639,10 @@ export interface AppToOpen { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2712,7 +2715,7 @@ export interface OpenBridgeErrorResponseMeta { * raised the original request. */ export interface OpenBridgeErrorResponsePayload { - error: OpenErrorMessage; + error: OpenErrorResponsePayload; } /** @@ -2761,7 +2764,7 @@ export interface OpenBridgeRequestPayload { * The application to open on the specified Desktop Agent */ app: AppToOpen; - context?: ContextElement; + context?: Context; } /** @@ -2920,16 +2923,17 @@ export interface PrivateChannelBroadcastAgentRequestPayload { /** * The context object that was the payload of a broadcast message. */ - context: ContextElement; + context: Context; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -2981,7 +2985,7 @@ export interface PrivateChannelBroadcastBridgeRequestPayload { /** * The context object that was the payload of a broadcast message. */ - context: ContextElement; + context: Context; } /** @@ -3041,9 +3045,10 @@ export type PrivateChannelEventListenerTypes = "onAddContextListener" | "onUnsub * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3147,9 +3152,10 @@ export interface PrivateChannelEventListenerRemovedAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3256,9 +3262,10 @@ export interface PrivateChannelOnAddContextListenerAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3364,9 +3371,10 @@ export interface PrivateChannelOnDisconnectAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3473,9 +3481,10 @@ export interface PrivateChannelOnUnsubscribeAgentRequestPayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3560,19 +3569,22 @@ export interface RaiseIntentAgentErrorResponseMeta { } /** + * Response to a raiseIntent request that resulted in an error + * * Error message payload containing an standardized error string. */ export interface RaiseIntentAgentErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3617,17 +3629,77 @@ export interface RaiseIntentAgentRequestMeta { */ export interface RaiseIntentAgentRequestPayload { app: AppDestinationIdentifier; - context: ContextElement; + context: Context; intent: string; } +/** + * Field that represents a destination App on a remote Desktop Agent that a request is to be + * sent to. + * + * Identifies a particular Desktop Agent in Desktop Agent Bridging scenarios + * where a request needs to be directed to a Desktop Agent rather than a specific app, or a + * response message is returned by the Desktop Agent (or more specifically its resolver) + * rather than a specific app. Used as a substitute for `AppIdentifier` in cases where no + * app details are available or are appropriate. + * + * Array of DesktopAgentIdentifiers for responses that were not returned to the bridge + * before the timeout or because an error occurred. May be omitted if all sources responded + * without errors. MUST include the `desktopAgent` field when returned by the bridge. + * + * Array of DesktopAgentIdentifiers for the sources that generated responses to the request. + * Will contain a single value for individual responses and multiple values for responses + * that were collated by the bridge. May be omitted if all sources errored. MUST include the + * `desktopAgent` field when returned by the bridge. + * + * Field that represents a destination Desktop Agent that a request is to be sent to. + * + * Identifies an application, or instance of an application, and is used to target FDC3 API + * calls, such as `fdc3.open` or `fdc3.raiseIntent` at specific applications or application + * instances. + * + * Will always include at least an `appId` field, which uniquely identifies a specific app. + * + * If the `instanceId` field is set then the `AppMetadata` object represents a specific + * instance of the application that may be addressed using that Id. + * + * Field that represents the source application that a request or response was received + * from. + * + * Identifier for the app instance that was selected (or started) to resolve the intent. + * `source.instanceId` MUST be set, indicating the specific app instance that + * received the intent. + */ +export interface AppDestinationIdentifier { + /** + * Used in Desktop Agent Bridging to attribute or target a message to a + * particular Desktop Agent. + * + * The Desktop Agent that the app is available on. Used in Desktop Agent Bridging to + * identify the Desktop Agent to target. + */ + desktopAgent: string; + /** + * The unique application identifier located within a specific application directory + * instance. An example of an appId might be 'app@sub.root' + */ + appId: string; + /** + * An optional instance identifier, indicating that this object represents a specific + * instance of the application described. + */ + instanceId?: string; + [property: string]: any; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3658,6 +3730,8 @@ export interface RaiseIntentAgentResponseMeta { } /** + * Response to a raiseIntent request that was successfully resolved + * * The message payload typically contains return values for FDC3 API functions. */ export interface RaiseIntentAgentResponsePayload { @@ -3736,11 +3810,13 @@ export interface RaiseIntentBridgeErrorResponseMeta { } /** + * Response to a raiseIntent request that resulted in an error + * * The error message payload contains details of an error return to the app or agent that * raised the original request. */ export interface RaiseIntentBridgeErrorResponsePayload { - error: ErrorMessage; + error: FindInstancesErrors; } /** @@ -3786,7 +3862,7 @@ export interface RaiseIntentBridgeRequestMeta { */ export interface RaiseIntentBridgeRequestPayload { app: AppDestinationIdentifier; - context: ContextElement; + context: Context; intent: string; } @@ -3822,6 +3898,8 @@ export interface RaiseIntentBridgeResponseMeta { } /** + * Response to a raiseIntent request that was successfully resolved + * * The message payload typically contains return values for FDC3 API functions. */ export interface RaiseIntentBridgeResponsePayload { @@ -3882,9 +3960,10 @@ export type RaiseIntentResultErrorMessage = "IntentHandlerRejected" | "NoResultR * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. * - * UUID for the request + * Unique identifier for a request or event message. Required in all message types * - * UUID for this specific response message. + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. */ /** @@ -3922,7 +4001,7 @@ export interface RaiseIntentResultAgentResponsePayload { } export interface IntentResult { - context?: ContextElement; + context?: Context; channel?: Channel; } @@ -4069,71 +4148,9 @@ export interface RaiseIntentResultBridgeResponsePayload { intentResult: IntentResult; } -/** - * The `fdc3.context` type defines the basic contract or "shape" for all data exchanged by - * FDC3 operations. As such, it is not really meant to be used on its own, but is imported - * by more specific type definitions (standardized or custom) to provide the structure and - * properties shared by all FDC3 context data types. - * - * The key element of FDC3 context types is their mandatory `type` property, which is used - * to identify what type of data the object represents, and what shape it has. - * - * The FDC3 context type, and all derived types, define the minimum set of fields a context - * data object of a particular type can be expected to have, but this can always be extended - * with custom fields as appropriate. - */ -export interface Context { - /** - * Context data objects may include a set of equivalent key-value pairs that can be used to - * help applications identify and look up the context type they receive in their own domain. - * The idea behind this design is that applications can provide as many equivalent - * identifiers to a target application as possible, e.g. an instrument may be represented by - * an ISIN, CUSIP or Bloomberg identifier. - * - * Identifiers do not make sense for all types of data, so the `id` property is therefore - * optional, but some derived types may choose to require at least one identifier. - * Identifier values SHOULD always be of type string. - */ - id?: { [key: string]: any }; - /** - * Context data objects may include a name property that can be used for more information, - * or display purposes. Some derived types may require the name object as mandatory, - * depending on use case. - */ - name?: string; - /** - * The type property is the only _required_ part of the FDC3 context data schema. The FDC3 - * [API](https://fdc3.finos.org/docs/api/spec) relies on the `type` property being present - * to route shared context data appropriately. - * - * FDC3 [Intents](https://fdc3.finos.org/docs/intents/spec) also register the context data - * types they support in an FDC3 [App - * Directory](https://fdc3.finos.org/docs/app-directory/overview), used for intent discovery - * and routing. - * - * Standardized FDC3 context types have well-known `type` properties prefixed with the - * `fdc3` namespace, e.g. `fdc3.instrument`. For non-standard types, e.g. those defined and - * used by a particular organization, the convention is to prefix them with an - * organization-specific namespace, e.g. `blackrock.fund`. - * - * See the [Context Data Specification](https://fdc3.finos.org/docs/context/spec) for more - * information about context data types. - */ - type: string; - [property: string]: any; -} - // Converts JSON strings to/from your types // and asserts the results of JSON.parse at runtime export class Convert { - public static toFDC3DesktopAgentAPISchema(json: string): any { - return cast(JSON.parse(json), "any"); - } - - public static fDC3DesktopAgentAPISchemaToJson(value: any): string { - return JSON.stringify(uncast(value, "any"), null, 2); - } - public static toAgentErrorResponseMessage(json: string): AgentErrorResponseMessage { return cast(JSON.parse(json), r("AgentErrorResponseMessage")); } @@ -4198,11 +4215,11 @@ export class Convert { return JSON.stringify(uncast(value, r("BroadcastBridgeRequest")), null, 2); } - public static toBridgingCommons(json: string): { [key: string]: any } { + public static toBridgeCommonDefinitions(json: string): { [key: string]: any } { return cast(JSON.parse(json), m("any")); } - public static bridgingCommonsToJson(value: { [key: string]: any }): string { + public static bridgeCommonDefinitionsToJson(value: { [key: string]: any }): string { return JSON.stringify(uncast(value, m("any")), null, 2); } @@ -4661,14 +4678,6 @@ export class Convert { public static raiseIntentResultBridgeResponseToJson(value: RaiseIntentResultBridgeResponse): string { return JSON.stringify(uncast(value, r("RaiseIntentResultBridgeResponse")), null, 2); } - - public static toContext(json: string): Context { - return cast(JSON.parse(json), r("Context")); - } - - public static contextToJson(value: Context): string { - return JSON.stringify(uncast(value, r("Context")), null, 2); - } } function invalidValue(typ: any, val: any, key: any, parent: any = ''): never { @@ -4922,9 +4931,9 @@ const typeMap: any = { ], "any"), "BroadcastAgentRequestPayload": o([ { json: "channelId", js: "channelId", typ: "" }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, ], false), - "ContextElement": o([ + "Context": o([ { json: "id", js: "id", typ: u(undefined, m("any")) }, { json: "name", js: "name", typ: u(undefined, "") }, { json: "type", js: "type", typ: "" }, @@ -4946,7 +4955,7 @@ const typeMap: any = { ], "any"), "BroadcastBridgeRequestPayload": o([ { json: "channelId", js: "channelId", typ: "" }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, ], false), "ConnectionStepMessage": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, @@ -4983,7 +4992,7 @@ const typeMap: any = { ], false), "ConnectionStep3HandshakePayload": o([ { json: "authToken", js: "authToken", typ: u(undefined, "") }, - { json: "channelsState", js: "channelsState", typ: m(a(r("ContextElement"))) }, + { json: "channelsState", js: "channelsState", typ: m(a(r("Context"))) }, { json: "implementationMetadata", js: "implementationMetadata", typ: r("ConnectingAgentImplementationMetadata") }, { json: "requestedName", js: "requestedName", typ: "" }, ], false), @@ -5024,7 +5033,7 @@ const typeMap: any = { "ConnectionStep6ConnectedAgentsUpdatePayload": o([ { json: "addAgent", js: "addAgent", typ: u(undefined, "") }, { json: "allAgents", js: "allAgents", typ: a(r("DesktopAgentImplementationMetadata")) }, - { json: "channelsState", js: "channelsState", typ: u(undefined, m(a(r("ContextElement")))) }, + { json: "channelsState", js: "channelsState", typ: u(undefined, m(a(r("Context")))) }, { json: "removeAgent", js: "removeAgent", typ: u(undefined, "") }, ], false), "DesktopAgentImplementationMetadata": o([ @@ -5036,7 +5045,7 @@ const typeMap: any = { ], false), "FindInstancesAgentErrorResponse": o([ { json: "meta", js: "meta", typ: r("FindInstancesAgentErrorResponseMeta") }, - { json: "payload", js: "payload", typ: r("FindInstancesAgentErrorResponsePayload") }, + { json: "payload", js: "payload", typ: r("PayloadClass") }, { json: "type", js: "type", typ: r("FindInstancesAgentErrorResponseType") }, ], false), "FindInstancesAgentErrorResponseMeta": o([ @@ -5044,8 +5053,8 @@ const typeMap: any = { { json: "responseUuid", js: "responseUuid", typ: "" }, { json: "timestamp", js: "timestamp", typ: Date }, ], false), - "FindInstancesAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + "PayloadClass": o([ + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindInstancesAgentRequest": o([ { json: "meta", js: "meta", typ: r("FindInstancesAgentRequestMeta") }, @@ -5072,15 +5081,10 @@ const typeMap: any = { { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, ], "any"), "FindInstancesAgentResponse": o([ - { json: "meta", js: "meta", typ: r("FindInstancesAgentResponseMeta") }, + { json: "meta", js: "meta", typ: r("AgentResponseMetadata") }, { json: "payload", js: "payload", typ: r("FindInstancesAgentResponsePayload") }, { json: "type", js: "type", typ: r("FindInstancesAgentErrorResponseType") }, ], false), - "FindInstancesAgentResponseMeta": o([ - { json: "requestUuid", js: "requestUuid", typ: "" }, - { json: "responseUuid", js: "responseUuid", typ: "" }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "FindInstancesAgentResponsePayload": o([ { json: "appIdentifiers", js: "appIdentifiers", typ: a(r("AppMetadata")) }, ], false), @@ -5111,7 +5115,7 @@ const typeMap: any = { ], false), "FindInstancesBridgeErrorResponse": o([ { json: "meta", js: "meta", typ: r("FindInstancesBridgeErrorResponseMeta") }, - { json: "payload", js: "payload", typ: r("FindInstancesBridgeErrorResponsePayload") }, + { json: "payload", js: "payload", typ: r("MessagePayload") }, { json: "type", js: "type", typ: r("FindInstancesAgentErrorResponseType") }, ], false), "FindInstancesBridgeErrorResponseMeta": o([ @@ -5121,8 +5125,8 @@ const typeMap: any = { { json: "responseUuid", js: "responseUuid", typ: "" }, { json: "timestamp", js: "timestamp", typ: Date }, ], false), - "FindInstancesBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + "MessagePayload": o([ + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindInstancesBridgeRequest": o([ { json: "meta", js: "meta", typ: r("FindInstancesBridgeRequestMeta") }, @@ -5144,18 +5148,10 @@ const typeMap: any = { { json: "app", js: "app", typ: r("AppIdentifier") }, ], false), "FindInstancesBridgeResponse": o([ - { json: "meta", js: "meta", typ: r("FindInstancesBridgeResponseMeta") }, + { json: "meta", js: "meta", typ: r("BridgeResponseMessageMeta") }, { json: "payload", js: "payload", typ: r("FindInstancesBridgeResponsePayload") }, { json: "type", js: "type", typ: r("FindInstancesAgentErrorResponseType") }, ], false), - "FindInstancesBridgeResponseMeta": o([ - { json: "errorDetails", js: "errorDetails", typ: u(undefined, a(r("ResponseErrorDetail"))) }, - { json: "errorSources", js: "errorSources", typ: u(undefined, a(r("DesktopAgentIdentifier"))) }, - { json: "requestUuid", js: "requestUuid", typ: "" }, - { json: "responseUuid", js: "responseUuid", typ: "" }, - { json: "sources", js: "sources", typ: u(undefined, a(r("DesktopAgentIdentifier"))) }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "FindInstancesBridgeResponsePayload": o([ { json: "appIdentifiers", js: "appIdentifiers", typ: a(r("AppMetadata")) }, ], false), @@ -5170,7 +5166,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "FindIntentAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindIntentAgentRequest": o([ { json: "meta", js: "meta", typ: r("FindIntentAgentRequestMeta") }, @@ -5184,7 +5180,7 @@ const typeMap: any = { { json: "destination", js: "destination", typ: u(undefined, r("BridgeParticipantIdentifier")) }, ], false), "FindIntentAgentRequestPayload": o([ - { json: "context", js: "context", typ: u(undefined, r("ContextElement")) }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "intent", js: "intent", typ: "" }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), @@ -5222,7 +5218,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "FindIntentBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindIntentBridgeRequest": o([ { json: "meta", js: "meta", typ: r("FindIntentBridgeRequestMeta") }, @@ -5236,7 +5232,7 @@ const typeMap: any = { { json: "destination", js: "destination", typ: u(undefined, r("BridgeParticipantIdentifier")) }, ], false), "FindIntentBridgeRequestPayload": o([ - { json: "context", js: "context", typ: u(undefined, r("ContextElement")) }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "intent", js: "intent", typ: "" }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), @@ -5267,7 +5263,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "FindIntentsByContextAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindIntentsByContextAgentRequest": o([ { json: "meta", js: "meta", typ: r("FindIntentsByContextAgentRequestMeta") }, @@ -5281,7 +5277,7 @@ const typeMap: any = { { json: "destination", js: "destination", typ: u(undefined, r("BridgeParticipantIdentifier")) }, ], false), "FindIntentsByContextAgentRequestPayload": o([ - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), "FindIntentsByContextAgentResponse": o([ @@ -5310,7 +5306,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "FindIntentsByContextBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "FindIntentsByContextBridgeRequest": o([ { json: "meta", js: "meta", typ: r("FindIntentsByContextBridgeRequestMeta") }, @@ -5324,7 +5320,7 @@ const typeMap: any = { { json: "destination", js: "destination", typ: u(undefined, r("BridgeParticipantIdentifier")) }, ], false), "FindIntentsByContextBridgeRequestPayload": o([ - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), "FindIntentsByContextBridgeResponse": o([ @@ -5354,7 +5350,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "GetAppMetadataAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "GetAppMetadataAgentRequest": o([ { json: "meta", js: "meta", typ: r("GetAppMetadataAgentRequestMeta") }, @@ -5368,9 +5364,9 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "GetAppMetadataAgentRequestPayload": o([ - { json: "app", js: "app", typ: r("AppDestinationIdentifier") }, + { json: "app", js: "app", typ: r("AppObject") }, ], false), - "AppDestinationIdentifier": o([ + "AppObject": o([ { json: "desktopAgent", js: "desktopAgent", typ: "" }, { json: "appId", js: "appId", typ: "" }, { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, @@ -5401,7 +5397,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "GetAppMetadataBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "GetAppMetadataBridgeRequest": o([ { json: "meta", js: "meta", typ: r("GetAppMetadataBridgeRequestMeta") }, @@ -5415,7 +5411,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "GetAppMetadataBridgeRequestPayload": o([ - { json: "app", js: "app", typ: r("AppDestinationIdentifier") }, + { json: "app", js: "app", typ: r("AppObject") }, ], false), "GetAppMetadataBridgeResponse": o([ { json: "meta", js: "meta", typ: r("GetAppMetadataBridgeResponseMeta") }, @@ -5444,7 +5440,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "OpenAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("OpenErrorMessage") }, + { json: "error", js: "error", typ: r("OpenErrorResponsePayload") }, ], false), "OpenAgentRequest": o([ { json: "meta", js: "meta", typ: r("OpenAgentRequestMeta") }, @@ -5459,7 +5455,7 @@ const typeMap: any = { ], false), "OpenAgentRequestPayload": o([ { json: "app", js: "app", typ: r("AppToOpen") }, - { json: "context", js: "context", typ: u(undefined, r("ContextElement")) }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, ], false), "AppToOpen": o([ { json: "desktopAgent", js: "desktopAgent", typ: "" }, @@ -5492,7 +5488,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "OpenBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("OpenErrorMessage") }, + { json: "error", js: "error", typ: r("OpenErrorResponsePayload") }, ], false), "OpenBridgeRequest": o([ { json: "meta", js: "meta", typ: r("OpenBridgeRequestMeta") }, @@ -5507,7 +5503,7 @@ const typeMap: any = { ], false), "OpenBridgeRequestPayload": o([ { json: "app", js: "app", typ: r("AppToOpen") }, - { json: "context", js: "context", typ: u(undefined, r("ContextElement")) }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, ], false), "OpenBridgeResponse": o([ { json: "meta", js: "meta", typ: r("OpenBridgeResponseMeta") }, @@ -5543,7 +5539,7 @@ const typeMap: any = { ], "any"), "PrivateChannelBroadcastAgentRequestPayload": o([ { json: "channelId", js: "channelId", typ: "" }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, ], false), "PrivateChannelBroadcastBridgeRequest": o([ { json: "meta", js: "meta", typ: r("PrivateChannelBroadcastBridgeRequestMeta") }, @@ -5558,7 +5554,7 @@ const typeMap: any = { ], false), "PrivateChannelBroadcastBridgeRequestPayload": o([ { json: "channelId", js: "channelId", typ: "" }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, ], false), "PrivateChannelEventListenerAddedAgentRequest": o([ { json: "meta", js: "meta", typ: r("PrivateChannelEventListenerAddedAgentRequestMeta") }, @@ -5719,7 +5715,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "RaiseIntentAgentErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "RaiseIntentAgentRequest": o([ { json: "meta", js: "meta", typ: r("RaiseIntentAgentRequestMeta") }, @@ -5734,9 +5730,14 @@ const typeMap: any = { ], false), "RaiseIntentAgentRequestPayload": o([ { json: "app", js: "app", typ: r("AppDestinationIdentifier") }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, { json: "intent", js: "intent", typ: "" }, ], false), + "AppDestinationIdentifier": o([ + { json: "desktopAgent", js: "desktopAgent", typ: "" }, + { json: "appId", js: "appId", typ: "" }, + { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, + ], "any"), "RaiseIntentAgentResponse": o([ { json: "meta", js: "meta", typ: r("RaiseIntentAgentResponseMeta") }, { json: "payload", js: "payload", typ: r("RaiseIntentAgentResponsePayload") }, @@ -5767,7 +5768,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "RaiseIntentBridgeErrorResponsePayload": o([ - { json: "error", js: "error", typ: r("ErrorMessage") }, + { json: "error", js: "error", typ: r("FindInstancesErrors") }, ], false), "RaiseIntentBridgeRequest": o([ { json: "meta", js: "meta", typ: r("RaiseIntentBridgeRequestMeta") }, @@ -5782,7 +5783,7 @@ const typeMap: any = { ], false), "RaiseIntentBridgeRequestPayload": o([ { json: "app", js: "app", typ: r("AppDestinationIdentifier") }, - { json: "context", js: "context", typ: r("ContextElement") }, + { json: "context", js: "context", typ: r("Context") }, { json: "intent", js: "intent", typ: "" }, ], false), "RaiseIntentBridgeResponse": o([ @@ -5828,7 +5829,7 @@ const typeMap: any = { { json: "intentResult", js: "intentResult", typ: r("IntentResult") }, ], false), "IntentResult": o([ - { json: "context", js: "context", typ: u(undefined, r("ContextElement")) }, + { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, ], false), "Channel": o([ @@ -5872,11 +5873,6 @@ const typeMap: any = { "RaiseIntentResultBridgeResponsePayload": o([ { json: "intentResult", js: "intentResult", typ: r("IntentResult") }, ], false), - "Context": o([ - { json: "id", js: "id", typ: u(undefined, m("any")) }, - { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: "" }, - ], "any"), "ResponseErrorDetail": [ "AccessDenied", "AgentDisconnected", @@ -5945,7 +5941,7 @@ const typeMap: any = { "ConnectionStep6ConnectedAgentsUpdateType": [ "connectedAgentsUpdate", ], - "ErrorMessage": [ + "FindInstancesErrors": [ "AgentDisconnected", "DesktopAgentNotFound", "IntentDeliveryFailed", @@ -5984,7 +5980,7 @@ const typeMap: any = { "GetAppMetadataAgentRequestType": [ "getAppMetadataRequest", ], - "OpenErrorMessage": [ + "OpenErrorResponsePayload": [ "AgentDisconnected", "AppNotFound", "AppTimeout", From 5c592c98d248e8b47800d2d16883e6a5f352ec72 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 23 Jul 2024 16:26:53 +0100 Subject: [PATCH 040/152] Fix privateChannelOnUnsubscribeEvent name and an issue with the typescript code generation --- package.json | 4 +- s2tQuicktypeUtil.js | 22 +- ...ivateChannelOnUnsubscribeEvent.schema.json | 2 +- src/api/BrowserTypes.ts | 540 +++++++++--------- 4 files changed, 277 insertions(+), 291 deletions(-) diff --git a/package.json b/package.json index c94bf5a7d..85489cf56 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,11 @@ "prepack": "npm run build", "typegen": "cd schemas && node ../s2tQuicktypeUtil.js context ../src/context/ContextTypes.ts", "typegen-browser": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json context/context.schema.json api ../src/api/BrowserTypes.ts", - "typegen-bridging": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json api/broadcastRequest.schema.json api/findInstancesRequest.schema.json api/findInstancesResponse.schema.json api/findIntentRequest.schema.json api/findIntentResponse.schema.json api/findIntentsByContextRequest.schema.json api/findIntentsByContextResponse.schema.json api/getAppMetadataRequest.schema.json api/getAppMetadataResponse.schema.json api/openRequest.schema.json api/openResponse.schema.json api/raiseIntentRequest.schema.json api/raiseIntentResponse.schema.json api/raiseIntentResultResponse.schema.json context/context.schema.json api/ bridging ../src/bridging/BridgingTypes.ts" + "typegen-bridging": "cd schemas && node ../s2tQuicktypeUtil.js api/api.schema.json api/common.schema.json api/broadcastRequest.schema.json api/findInstancesRequest.schema.json api/findInstancesResponse.schema.json api/findIntentRequest.schema.json api/findIntentResponse.schema.json api/findIntentsByContextRequest.schema.json api/findIntentsByContextResponse.schema.json api/getAppMetadataRequest.schema.json api/getAppMetadataResponse.schema.json api/openRequest.schema.json api/openResponse.schema.json api/raiseIntentRequest.schema.json api/raiseIntentResponse.schema.json api/raiseIntentResultResponse.schema.json context/context.schema.json bridging ../src/bridging/BridgingTypes.ts" }, "husky": { "hooks": { - "pre-commit": "npm run lint" + "pre-commit": "npm run prebuild" } }, "prettier": { diff --git a/s2tQuicktypeUtil.js b/s2tQuicktypeUtil.js index 4195a7f09..03febea22 100644 --- a/s2tQuicktypeUtil.js +++ b/s2tQuicktypeUtil.js @@ -16,21 +16,23 @@ const path = require('path'); const fs = require('fs'); +const { forEachChild } = require('typescript'); const exec = require('child_process').exec; const args = process.argv.slice(2); const outputFile = args.pop(); const inputs = args; +const toProcess = []; console.log('Inputs schema files: ' + inputs.join(' | ')); console.log('Output file argument: ' + outputFile); let sources = ''; let additionalSchemaFiles = '' -let dirIndex = 0; //skip duplicate paths (we might want to specify some files to go first, and might duplicate them) -const paths = new Set(); +const allPaths = new Set(); + const addAPath = (aPath,paths,sources,type) => { if (!paths.has(aPath)) { paths.add(aPath) @@ -41,20 +43,22 @@ const addAPath = (aPath,paths,sources,type) => { } } -while (dirIndex < inputs.length) { - if (inputs[dirIndex].endsWith('.schema.json')) { +//process the content of folders to produce code for top-level types +let inputIndex = 0; +while (inputIndex < inputs.length) { + if (inputs[inputIndex].endsWith('.schema.json')) { //add individual files with -S as additional schema files used in references (rather than ones that need a top-level output) - sources = addAPath(path.join(inputs[dirIndex]),paths,additionalSchemaFiles, "-S"); + additionalSchemaFiles = addAPath(path.join(inputs[inputIndex]),allPaths,additionalSchemaFiles, "-S"); } else { - fs.readdirSync(inputs[dirIndex], { withFileTypes: true }).forEach(file => { + fs.readdirSync(inputs[inputIndex], { withFileTypes: true }).forEach(file => { if (file.isDirectory()) { - inputs.push(path.join(inputs[dirIndex], file.name)); + inputs.push(path.join(inputs[inputIndex], file.name)); } else if (file.name.endsWith('.schema.json')) { - sources = addAPath(path.join(inputs[dirIndex], file.name),paths,sources, "--src"); + sources = addAPath(path.join(inputs[inputIndex], file.name),allPaths,sources, "--src"); } }); } - dirIndex++; + inputIndex++; } // Normalise path to local quicktype executable. diff --git a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json index c2a93a21c..2ae1781b7 100644 --- a/schemas/api/privateChannelOnUnsubscribeEvent.schema.json +++ b/schemas/api/privateChannelOnUnsubscribeEvent.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelOnUnsubscribeEvent.schema.json", "type": "object", - "title": "PrivateChannelOnUnsubscribeEvent Event", + "title": "PrivateChannelOnUnsubscribe Event", "description": "An event message from the Desktop Agent to an app indicating that another app has unsubscribed a context listener from a specific PrivateChannel.", "allOf": [ { diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 644065548..389dc4169 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,16 +1,14 @@ // To parse this data: // -// import { Convert, AppRequestMessage, AgentResponseMessage, AgentEventMessage, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEventEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // -// const fDC3DesktopAgentAPISchemas = Convert.toFDC3DesktopAgentAPISchemas(json); -// const commonDefinitions = Convert.toCommonDefinitions(json); -// const appRequestMessage = Convert.toAppRequestMessage(json); -// const agentResponseMessage = Convert.toAgentResponseMessage(json); -// const agentEventMessage = Convert.toAgentEventMessage(json); // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); // const addIntentListenerRequest = Convert.toAddIntentListenerRequest(json); // const addIntentListenerResponse = Convert.toAddIntentListenerResponse(json); +// const agentEventMessage = Convert.toAgentEventMessage(json); +// const agentResponseMessage = Convert.toAgentResponseMessage(json); +// const appRequestMessage = Convert.toAppRequestMessage(json); // const broadcastEvent = Convert.toBroadcastEvent(json); // const broadcastRequest = Convert.toBroadcastRequest(json); // const broadcastResponse = Convert.toBroadcastResponse(json); @@ -61,7 +59,7 @@ // const privateChannelDisconnectResponse = Convert.toPrivateChannelDisconnectResponse(json); // const privateChannelOnAddContextListenerEvent = Convert.toPrivateChannelOnAddContextListenerEvent(json); // const privateChannelOnDisconnectEvent = Convert.toPrivateChannelOnDisconnectEvent(json); -// const privateChannelOnUnsubscribeEventEvent = Convert.toPrivateChannelOnUnsubscribeEventEvent(json); +// const privateChannelOnUnsubscribeEvent = Convert.toPrivateChannelOnUnsubscribeEvent(json); // const privateChannelUnsubscribeEventListenerRequest = Convert.toPrivateChannelUnsubscribeEventListenerRequest(json); // const privateChannelUnsubscribeEventListenerResponse = Convert.toPrivateChannelUnsubscribeEventListenerResponse(json); // const raiseIntentForContextRequest = Convert.toRaiseIntentForContextRequest(json); @@ -81,28 +79,31 @@ // match the expected interface, even if the JSON is valid. /** + * A request to add a context listener to a specified Channel OR to the current user + * channel. + * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface AppRequestMessage { +export interface AddContextListenerRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ - meta: AppRequestMessageMeta; + meta: AddContextListenerRequestMeta; /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: { [key: string]: any }; + payload: AddContextListenerRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: RequestMessageType; + type: "addContextListenerRequest"; } /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ -export interface AppRequestMessageMeta { +export interface AddContextListenerRequestMeta { requestUuid: string; /** * Field that represents the source application that a request or response was received @@ -161,38 +162,55 @@ export interface AppIdentifier { [property: string]: any; } +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface AddContextListenerRequestPayload { + /** + * The id of the channel to add the listener to or `null` indicating that it should listen + * to the current user channel (at the time of broadcast). + */ + channelId: null | string; + /** + * The type of context to listen for OR `null` indicating that it should listen to all + * context types. + */ + contextType: null | string; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** + * A response to a addContextListener request. + * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface AgentResponseMessage { +export interface AddContextListenerResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ - meta: AgentResponseMessageMeta; + meta: AddContextListenerResponseMeta; /** * A payload for a response to an API call that will contain any return values or an `error` * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: AgentResponseMessageResponsePayload; + payload: AddContextListenerResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: ResponseMessageType; + type: "addContextListenerResponse"; } /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ -export interface AgentResponseMessageMeta { +export interface AddContextListenerResponseMeta { requestUuid: string; responseUuid: string; /** @@ -208,9 +226,9 @@ export interface AgentResponseMessageMeta { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface AgentResponseMessageResponsePayload { - error?: ResponsePayloadError; - [property: string]: any; +export interface AddContextListenerResponsePayload { + error?: PurpleError; + listenerUUID?: string; } /** @@ -221,54 +239,19 @@ export interface AgentResponseMessageResponsePayload { * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). */ -export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "IntentHandlerRejected" | "NoResultReturned" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; - -/** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ -export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; - -/** - * A message from a Desktop Agent to an FDC3-enabled app representing an event. - */ -export interface AgentEventMessage { - /** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ - meta: AgentEventMessageMeta; - /** - * The message payload contains details of the event that the app is being notified about. - */ - payload: { [key: string]: any }; - /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ - type: EventMessageType; -} - -/** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ -export interface AgentEventMessageMeta { - eventUuid: string; - timestamp: Date; -} +export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound"; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; /** - * A request to add a context listener to a specified Channel OR to the current user - * channel. + * A request to add an Intent listener for a specified intent type. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface AddContextListenerRequest { +export interface AddIntentListenerRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -276,43 +259,22 @@ export interface AddContextListenerRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: AddContextListenerRequestPayload; + payload: AddIntentListenerRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "addContextListenerRequest"; -} - -/** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. - */ -export interface AddContextListenerRequestMeta { - requestUuid: string; - /** - * Field that represents the source application that a request or response was received - * from. Please note that this may be set by an app or Desktop Agent proxy for debugging - * purposes but a Desktop Agent should make its own determination of the source of a message - * to avoid spoofing. - */ - source?: AppIdentifier; - timestamp: Date; + type: "addIntentListenerRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface AddContextListenerRequestPayload { - /** - * The id of the channel to add the listener to or `null` indicating that it should listen - * to the current user channel (at the time of broadcast). - */ - channelId: null | string; +export interface AddIntentListenerRequestPayload { /** - * The type of context to listen for OR `null` indicating that it should listen to all - * context types. + * The name of the intent to listen for. */ - contextType: null | string; + intent: string; } /** @@ -321,12 +283,12 @@ export interface AddContextListenerRequestPayload { */ /** - * A response to a addContextListener request. + * A response to a addIntentListener request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface AddContextListenerResponse { +export interface AddIntentListenerResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -336,26 +298,12 @@ export interface AddContextListenerResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: AddContextListenerResponsePayload; + payload: AddIntentListenerResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "addContextListenerResponse"; -} - -/** - * Metadata for messages sent by a Desktop Agent to an App in response to an API call - */ -export interface AddContextListenerResponseMeta { - requestUuid: string; - responseUuid: string; - /** - * Field that represents the source application that the request being responded to was - * received from, for debugging purposes. - */ - source?: AppIdentifier; - timestamp: Date; + type: "addIntentListenerResponse"; } /** @@ -363,9 +311,10 @@ export interface AddContextListenerResponseMeta { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface AddContextListenerResponsePayload { - error?: PurpleError; +export interface AddIntentListenerResponsePayload { + error?: FluffyError; listenerUUID?: string; + [property: string]: any; } /** @@ -376,7 +325,7 @@ export interface AddContextListenerResponsePayload { * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). */ -export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound"; +export type FluffyError = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution"; /** * Identifies the type of the message and it is typically set to the FDC3 function name that @@ -384,63 +333,72 @@ export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" */ /** - * A request to add an Intent listener for a specified intent type. - * - * A request message from an FDC3-enabled app to a Desktop Agent. + * A message from a Desktop Agent to an FDC3-enabled app representing an event. */ -export interface AddIntentListenerRequest { +export interface AgentEventMessage { /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddContextListenerRequestMeta; + meta: AgentEventMessageMeta; /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload contains details of the event that the app is being notified about. */ - payload: AddIntentListenerRequestPayload; + payload: { [key: string]: any }; /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "addIntentListenerRequest"; + type: EventMessageType; } /** - * The message payload typically contains the arguments to FDC3 API functions. + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ -export interface AddIntentListenerRequestPayload { - /** - * The name of the intent to listen for. - */ - intent: string; +export interface AgentEventMessageMeta { + eventUuid: string; + timestamp: Date; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ +export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; /** - * A response to a addIntentListener request. - * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface AddIntentListenerResponse { +export interface AgentResponseMessage { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ - meta: AddContextListenerResponseMeta; + meta: AgentResponseMessageMeta; /** * A payload for a response to an API call that will contain any return values or an `error` * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: AddIntentListenerResponsePayload; + payload: AgentResponseMessageResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "addIntentListenerResponse"; + type: ResponseMessageType; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ +export interface AgentResponseMessageMeta { + requestUuid: string; + responseUuid: string; + /** + * Field that represents the source application that the request being responded to was + * received from, for debugging purposes. + */ + source?: AppIdentifier; + timestamp: Date; } /** @@ -448,9 +406,8 @@ export interface AddIntentListenerResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface AddIntentListenerResponsePayload { - error?: FluffyError; - listenerUUID?: string; +export interface AgentResponseMessageResponsePayload { + error?: ResponsePayloadError; [property: string]: any; } @@ -462,12 +419,53 @@ export interface AddIntentListenerResponsePayload { * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). */ -export type FluffyError = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution"; +export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "IntentHandlerRejected" | "NoResultReturned" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ +export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; + +/** + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface AppRequestMessage { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AppRequestMessageMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: { [key: string]: any }; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: RequestMessageType; +} + +/** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ +export interface AppRequestMessageMeta { + requestUuid: string; + /** + * Field that represents the source application that a request or response was received + * from. Please note that this may be set by an app or Desktop Agent proxy for debugging + * purposes but a Desktop Agent should make its own determination of the source of a message + * to avoid spoofing. + */ + source?: AppIdentifier; + timestamp: Date; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ +export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -2732,7 +2730,7 @@ export interface PrivateChannelOnDisconnectEventPayload { * * A message from a Desktop Agent to an FDC3-enabled app representing an event. */ -export interface PrivateChannelOnUnsubscribeEventEvent { +export interface PrivateChannelOnUnsubscribeEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ @@ -2740,7 +2738,7 @@ export interface PrivateChannelOnUnsubscribeEventEvent { /** * The message payload contains details of the event that the app is being notified about. */ - payload: PrivateChannelOnUnsubscribeEventEventPayload; + payload: PrivateChannelOnUnsubscribeEventPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -2751,7 +2749,7 @@ export interface PrivateChannelOnUnsubscribeEventEvent { /** * The message payload contains details of the event that the app is being notified about. */ -export interface PrivateChannelOnUnsubscribeEventEventPayload { +export interface PrivateChannelOnUnsubscribeEventPayload { /** * The type of the context listener unsubscribed from the channel by another app, or null if * it was listening to all types. @@ -3358,46 +3356,6 @@ export type ConnectionStepMessageType = "WCP1Hello" | "WCP2LoadUrl" | "WCP3Hands // Converts JSON strings to/from your types // and asserts the results of JSON.parse at runtime export class Convert { - public static toFDC3DesktopAgentAPISchemas(json: string): any { - return cast(JSON.parse(json), "any"); - } - - public static fDC3DesktopAgentAPISchemasToJson(value: any): string { - return JSON.stringify(uncast(value, "any"), null, 2); - } - - public static toCommonDefinitions(json: string): { [key: string]: any } { - return cast(JSON.parse(json), m("any")); - } - - public static commonDefinitionsToJson(value: { [key: string]: any }): string { - return JSON.stringify(uncast(value, m("any")), null, 2); - } - - public static toAppRequestMessage(json: string): AppRequestMessage { - return cast(JSON.parse(json), r("AppRequestMessage")); - } - - public static appRequestMessageToJson(value: AppRequestMessage): string { - return JSON.stringify(uncast(value, r("AppRequestMessage")), null, 2); - } - - public static toAgentResponseMessage(json: string): AgentResponseMessage { - return cast(JSON.parse(json), r("AgentResponseMessage")); - } - - public static agentResponseMessageToJson(value: AgentResponseMessage): string { - return JSON.stringify(uncast(value, r("AgentResponseMessage")), null, 2); - } - - public static toAgentEventMessage(json: string): AgentEventMessage { - return cast(JSON.parse(json), r("AgentEventMessage")); - } - - public static agentEventMessageToJson(value: AgentEventMessage): string { - return JSON.stringify(uncast(value, r("AgentEventMessage")), null, 2); - } - public static toAddContextListenerRequest(json: string): AddContextListenerRequest { return cast(JSON.parse(json), r("AddContextListenerRequest")); } @@ -3430,6 +3388,30 @@ export class Convert { return JSON.stringify(uncast(value, r("AddIntentListenerResponse")), null, 2); } + public static toAgentEventMessage(json: string): AgentEventMessage { + return cast(JSON.parse(json), r("AgentEventMessage")); + } + + public static agentEventMessageToJson(value: AgentEventMessage): string { + return JSON.stringify(uncast(value, r("AgentEventMessage")), null, 2); + } + + public static toAgentResponseMessage(json: string): AgentResponseMessage { + return cast(JSON.parse(json), r("AgentResponseMessage")); + } + + public static agentResponseMessageToJson(value: AgentResponseMessage): string { + return JSON.stringify(uncast(value, r("AgentResponseMessage")), null, 2); + } + + public static toAppRequestMessage(json: string): AppRequestMessage { + return cast(JSON.parse(json), r("AppRequestMessage")); + } + + public static appRequestMessageToJson(value: AppRequestMessage): string { + return JSON.stringify(uncast(value, r("AppRequestMessage")), null, 2); + } + public static toBroadcastEvent(json: string): BroadcastEvent { return cast(JSON.parse(json), r("BroadcastEvent")); } @@ -3830,12 +3812,12 @@ export class Convert { return JSON.stringify(uncast(value, r("PrivateChannelOnDisconnectEvent")), null, 2); } - public static toPrivateChannelOnUnsubscribeEventEvent(json: string): PrivateChannelOnUnsubscribeEventEvent { - return cast(JSON.parse(json), r("PrivateChannelOnUnsubscribeEventEvent")); + public static toPrivateChannelOnUnsubscribeEvent(json: string): PrivateChannelOnUnsubscribeEvent { + return cast(JSON.parse(json), r("PrivateChannelOnUnsubscribeEvent")); } - public static privateChannelOnUnsubscribeEventEventToJson(value: PrivateChannelOnUnsubscribeEventEvent): string { - return JSON.stringify(uncast(value, r("PrivateChannelOnUnsubscribeEventEvent")), null, 2); + public static privateChannelOnUnsubscribeEventToJson(value: PrivateChannelOnUnsubscribeEvent): string { + return JSON.stringify(uncast(value, r("PrivateChannelOnUnsubscribeEvent")), null, 2); } public static toPrivateChannelUnsubscribeEventListenerRequest(json: string): PrivateChannelUnsubscribeEventListenerRequest { @@ -4104,44 +4086,6 @@ function r(name: string) { } const typeMap: any = { - "AppRequestMessage": o([ - { json: "meta", js: "meta", typ: r("AppRequestMessageMeta") }, - { json: "payload", js: "payload", typ: m("any") }, - { json: "type", js: "type", typ: r("RequestMessageType") }, - ], false), - "AppRequestMessageMeta": o([ - { json: "requestUuid", js: "requestUuid", typ: "" }, - { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), - "AppIdentifier": o([ - { json: "appId", js: "appId", typ: "" }, - { json: "desktopAgent", js: "desktopAgent", typ: u(undefined, "") }, - { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, - ], "any"), - "AgentResponseMessage": o([ - { json: "meta", js: "meta", typ: r("AgentResponseMessageMeta") }, - { json: "payload", js: "payload", typ: r("AgentResponseMessageResponsePayload") }, - { json: "type", js: "type", typ: r("ResponseMessageType") }, - ], false), - "AgentResponseMessageMeta": o([ - { json: "requestUuid", js: "requestUuid", typ: "" }, - { json: "responseUuid", js: "responseUuid", typ: "" }, - { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), - "AgentResponseMessageResponsePayload": o([ - { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, - ], "any"), - "AgentEventMessage": o([ - { json: "meta", js: "meta", typ: r("AgentEventMessageMeta") }, - { json: "payload", js: "payload", typ: m("any") }, - { json: "type", js: "type", typ: r("EventMessageType") }, - ], false), - "AgentEventMessageMeta": o([ - { json: "eventUuid", js: "eventUuid", typ: "" }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "AddContextListenerRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("AddContextListenerRequestPayload") }, @@ -4152,6 +4096,11 @@ const typeMap: any = { { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, { json: "timestamp", js: "timestamp", typ: Date }, ], false), + "AppIdentifier": o([ + { json: "appId", js: "appId", typ: "" }, + { json: "desktopAgent", js: "desktopAgent", typ: u(undefined, "") }, + { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, + ], "any"), "AddContextListenerRequestPayload": o([ { json: "channelId", js: "channelId", typ: u(null, "") }, { json: "contextType", js: "contextType", typ: u(null, "") }, @@ -4188,6 +4137,39 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("FluffyError")) }, { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, ], "any"), + "AgentEventMessage": o([ + { json: "meta", js: "meta", typ: r("AgentEventMessageMeta") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("EventMessageType") }, + ], false), + "AgentEventMessageMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AgentResponseMessage": o([ + { json: "meta", js: "meta", typ: r("AgentResponseMessageMeta") }, + { json: "payload", js: "payload", typ: r("AgentResponseMessageResponsePayload") }, + { json: "type", js: "type", typ: r("ResponseMessageType") }, + ], false), + "AgentResponseMessageMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "responseUuid", js: "responseUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AgentResponseMessageResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + ], "any"), + "AppRequestMessage": o([ + { json: "meta", js: "meta", typ: r("AppRequestMessageMeta") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("RequestMessageType") }, + ], false), + "AppRequestMessageMeta": o([ + { json: "requestUuid", js: "requestUuid", typ: "" }, + { json: "source", js: "source", typ: u(undefined, r("AppIdentifier")) }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), "BroadcastEvent": o([ { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("BroadcastEventPayload") }, @@ -4671,12 +4653,12 @@ const typeMap: any = { "PrivateChannelOnDisconnectEventPayload": o([ { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), - "PrivateChannelOnUnsubscribeEventEvent": o([ + "PrivateChannelOnUnsubscribeEvent": o([ { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, - { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventEventPayload") }, - { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventEventType") }, + { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventPayload") }, + { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventType") }, ], false), - "PrivateChannelOnUnsubscribeEventEventPayload": o([ + "PrivateChannelOnUnsubscribeEventPayload": o([ { json: "contextType", js: "contextType", typ: u(null, "") }, { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), @@ -4816,30 +4798,42 @@ const typeMap: any = { { json: "payload", js: "payload", typ: m("any") }, { json: "type", js: "type", typ: r("ConnectionStepMessageType") }, ], false), - "RequestMessageType": [ + "AddContextListenerRequestType": [ "addContextListenerRequest", + ], + "PurpleError": [ + "AccessDenied", + "CreationFailed", + "MalformedContext", + "NoChannelFound", + ], + "AddContextListenerResponseType": [ + "addContextListenerResponse", + ], + "AddIntentListenerRequestType": [ "addIntentListenerRequest", - "broadcastRequest", - "contextListenerUnsubscribeRequest", - "createPrivateChannelRequest", - "findInstancesRequest", - "findIntentRequest", - "findIntentsByContextRequest", - "getAppMetadataRequest", - "getCurrentChannelRequest", - "getCurrentContextRequest", - "getInfoRequest", - "getOrCreateChannelRequest", - "getUserChannelsRequest", - "intentListenerUnsubscribeRequest", - "joinUserChannelRequest", - "leaveCurrentChannelRequest", - "openRequest", - "privateChannelAddEventListenerRequest", - "privateChannelDisconnectRequest", - "privateChannelUnsubscribeEventListenerRequest", - "raiseIntentForContextRequest", - "raiseIntentRequest", + ], + "FluffyError": [ + "DesktopAgentNotFound", + "IntentDeliveryFailed", + "MalformedContext", + "NoAppsFound", + "ResolverTimeout", + "ResolverUnavailable", + "TargetAppUnavailable", + "TargetInstanceUnavailable", + "UserCancelledResolution", + ], + "AddIntentListenerResponseType": [ + "addIntentListenerResponse", + ], + "EventMessageType": [ + "broadcastEvent", + "channelChangedEvent", + "intentEvent", + "privateChannelOnAddContextListenerEvent", + "privateChannelOnDisconnectEvent", + "privateChannelOnUnsubscribeEvent", ], "ResponsePayloadError": [ "AccessDenied", @@ -4890,42 +4884,30 @@ const typeMap: any = { "raiseIntentResponse", "raiseIntentResultResponse", ], - "EventMessageType": [ - "broadcastEvent", - "channelChangedEvent", - "intentEvent", - "privateChannelOnAddContextListenerEvent", - "privateChannelOnDisconnectEvent", - "privateChannelOnUnsubscribeEvent", - ], - "AddContextListenerRequestType": [ + "RequestMessageType": [ "addContextListenerRequest", - ], - "PurpleError": [ - "AccessDenied", - "CreationFailed", - "MalformedContext", - "NoChannelFound", - ], - "AddContextListenerResponseType": [ - "addContextListenerResponse", - ], - "AddIntentListenerRequestType": [ "addIntentListenerRequest", - ], - "FluffyError": [ - "DesktopAgentNotFound", - "IntentDeliveryFailed", - "MalformedContext", - "NoAppsFound", - "ResolverTimeout", - "ResolverUnavailable", - "TargetAppUnavailable", - "TargetInstanceUnavailable", - "UserCancelledResolution", - ], - "AddIntentListenerResponseType": [ - "addIntentListenerResponse", + "broadcastRequest", + "contextListenerUnsubscribeRequest", + "createPrivateChannelRequest", + "findInstancesRequest", + "findIntentRequest", + "findIntentsByContextRequest", + "getAppMetadataRequest", + "getCurrentChannelRequest", + "getCurrentContextRequest", + "getInfoRequest", + "getOrCreateChannelRequest", + "getUserChannelsRequest", + "intentListenerUnsubscribeRequest", + "joinUserChannelRequest", + "leaveCurrentChannelRequest", + "openRequest", + "privateChannelAddEventListenerRequest", + "privateChannelDisconnectRequest", + "privateChannelUnsubscribeEventListenerRequest", + "raiseIntentForContextRequest", + "raiseIntentRequest", ], "BroadcastEventType": [ "broadcastEvent", @@ -5132,7 +5114,7 @@ const typeMap: any = { "PrivateChannelOnDisconnectEventType": [ "privateChannelOnDisconnectEvent", ], - "PrivateChannelOnUnsubscribeEventEventType": [ + "PrivateChannelOnUnsubscribeEventType": [ "privateChannelOnUnsubscribeEvent", ], "PrivateChannelUnsubscribeEventListenerRequestType": [ From 49a7f2bc924bf535c7edb11469370399dce25d59 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 23 Jul 2024 17:44:05 +0100 Subject: [PATCH 041/152] regenerating after merging main (IntentMetadata.displayName is optional) --- src/api/BrowserTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 389dc4169..83f74b83b 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1209,7 +1209,7 @@ export interface IntentMetadata { /** * Display name for the intent. */ - displayName: string; + displayName?: string; /** * The unique name of the intent that can be invoked by the raiseIntent call */ @@ -4319,7 +4319,7 @@ const typeMap: any = { { json: "intent", js: "intent", typ: r("IntentMetadata") }, ], false), "IntentMetadata": o([ - { json: "displayName", js: "displayName", typ: "" }, + { json: "displayName", js: "displayName", typ: u(undefined, "") }, { json: "name", js: "name", typ: "" }, ], false), "FindIntentsByContextRequest": o([ From 5deeb5da870ee5b31e0c573f60c81e234a503be1 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 26 Jul 2024 11:31:22 +0100 Subject: [PATCH 042/152] Fix issue in privateChanneladdEventListenerRequestSchema.json --- ...ChanneladdEventListenerRequest.schema.json | 19 +++++----------- src/api/BrowserTypes.ts | 22 ++++++++++++++----- .../next/context/instrument.schema.json | 22 +++++++++---------- 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/schemas/api/privateChanneladdEventListenerRequest.schema.json b/schemas/api/privateChanneladdEventListenerRequest.schema.json index a3d7e582d..2c07afb86 100644 --- a/schemas/api/privateChanneladdEventListenerRequest.schema.json +++ b/schemas/api/privateChanneladdEventListenerRequest.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/privateChannelAddEventListenerRequest.schema.json", "type": "object", "title": "PrivateChannelAddEventListener Request", - "description": "An event message from the Desktop Agent to an app indicating that another app has added a context listener to a specific PrivateChannel.", + "description": "A request to add an event listener to a specific PrivateChannel.", "allOf": [ { "$ref": "appRequest.schema.json" @@ -36,23 +36,16 @@ "description": "The Id of the PrivateChannel that the listener should be added to.", "type": "string" }, - "contextType": { - "title": "Context type", - "description": "The type of the context listener to add to the channel, or null if it should listen to all types.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] + "listenerType": { + "title": "Event listener type", + "description": "The type of PrivateChannel event that the listener should be applied to.", + "$ref": "common.schema.json#/$defs/PrivateChannelEventListenerTypes" } }, "additionalProperties": false, "required": [ "privateChannelId", - "contextType" + "listenerType" ] } } diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 83f74b83b..24eb0b8c6 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -2489,8 +2489,7 @@ export type OpenErrorResponsePayload = "MalformedContext" | "AppNotFound" | "App */ /** - * An event message from the Desktop Agent to an app indicating that another app has added a - * context listener to a specific PrivateChannel. + * A request to add an event listener to a specific PrivateChannel. * * A request message from an FDC3-enabled app to a Desktop Agent. */ @@ -2515,16 +2514,22 @@ export interface PrivateChannelAddEventListenerRequest { */ export interface TPayload { /** - * The type of the context listener to add to the channel, or null if it should listen to - * all types. + * The type of PrivateChannel event that the listener should be applied to. */ - contextType: null | string; + listenerType: PrivateChannelEventListenerTypes; /** * The Id of the PrivateChannel that the listener should be added to. */ privateChannelId: string; } +/** + * The type of PrivateChannel event that the listener should be applied to. + * + * Event listener type names for Private Channel events + */ +export type PrivateChannelEventListenerTypes = "onAddContextListener" | "onUnsubscribe" | "onDisconnect"; + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. @@ -4608,7 +4613,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("PrivateChannelAddEventListenerRequestType") }, ], false), "TPayload": o([ - { json: "contextType", js: "contextType", typ: u(null, "") }, + { json: "listenerType", js: "listenerType", typ: r("PrivateChannelEventListenerTypes") }, { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelAddEventListenerResponse": o([ @@ -5096,6 +5101,11 @@ const typeMap: any = { "OpenResponseType": [ "openResponse", ], + "PrivateChannelEventListenerTypes": [ + "onAddContextListener", + "onDisconnect", + "onUnsubscribe", + ], "PrivateChannelAddEventListenerRequestType": [ "privateChannelAddEventListenerRequest", ], diff --git a/website/static/schemas/next/context/instrument.schema.json b/website/static/schemas/next/context/instrument.schema.json index 7ea8435d8..d38d3a3f3 100644 --- a/website/static/schemas/next/context/instrument.schema.json +++ b/website/static/schemas/next/context/instrument.schema.json @@ -19,42 +19,42 @@ "BBG": { "type": "string", "title": "Bloomberg security", - "description": "" + "description": "https://www.bloomberg.com/" }, "CUSIP": { "type": "string", "title": "CUSIP", - "description": "" + "description": "https://www.cusip.com/" }, "FDS_ID": { "type": "string", "title": "FactSet Permanent Security Identifier", - "description": "" + "description": "https://www.factset.com/" }, "FIGI": { "type": "string", "title": "Open FIGI", - "description": "" + "description": "https://www.openfigi.com/" }, "ISIN": { "type": "string", "title": "ISIN", - "description": "" + "description": "https://www.isin.org/" }, "PERMID": { "type": "string", "title": "Refinitiv PERMID", - "description": "" + "description": "https://permid.org/" }, "RIC": { "type": "string", "title": "Refinitiv Identification Code", - "description": " " + "description": "https://www.refinitiv.com/" }, "SEDOL": { "type": "string", "title": "SEDOL", - "description": "" + "description": "https://www.lseg.com/sedol" }, "ticker": { "type": "string", @@ -70,7 +70,7 @@ "MIC": { "type": "string", "title": "Market Identifier Code", - "description": "" + "description": "https://en.wikipedia.org/wiki/Market_Identifier_Code" }, "name": { "type": "string", @@ -80,12 +80,12 @@ "COUNTRY_ISOALPHA2": { "type": "string", "title": "Country ISO Code", - "description": "" + "description": "https://www.iso.org/iso-3166-country-codes.html" }, "BBG": { "type": "string", "title": "Bloomberg Market Identifier", - "description": "" + "description": "https://www.bloomberg.com/" } }, "unevaluatedProperties": { From aa276dd85a879bb3c91ea51e6b9ec4b64506dba2 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 26 Jul 2024 18:08:43 +0100 Subject: [PATCH 043/152] small tweaks to supported-platforms and glossary (WIP) --- docs/api/supported-platforms.md | 27 +++++++++++++++++---------- docs/fdc3-glossary.md | 2 ++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 38ddda9e5..8853e32f4 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -9,25 +9,32 @@ There are two main categories of platform: web and native, both of which are des ## Web -For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the desktop agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). +For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): -- **Preload**: Used where the desktop agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. -- **Proxy**: Used when running in standard web browser (without an extension) and the desktop agent has to run in a different window or frame to the application and must be communicated with via Cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of Cross-document messaging, allowing the application to work with the FDC3 API directly. +- **Desktop Agent Preload**: Used where the Desktop Agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. +- **Desktop Agent Proxy**: Used when running in a standard web browser (without a browser extension or similar customization). The Desktop Agent will often be running in a different window or frame to the application and must be communicated with via cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of cross-document messaging, allowing the application to work with the FDC3 API directly. The FDC3 Standard defines a [Web Connection Protocol (WCP)](specs/webConnectionProtocol) that allows apps to work with either interface, by detecting which is applicable. The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. -Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support either of the standardized interfaces. +Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support any of the standardized interfaces. :::note -In prior versions of FDC3 (<= 2.1) Apps were required to use the 'Preload' interface, i.e. they relied on the existence of the `window.fdc3` object, which meant that apps running in a standard web browser had to import libraries specific to the Desktop Agent implementation in use. From FDC3 2.2 onwards the 'Proxy' interface is available, which allows apps in a standard web browser to connect to any Desktop Agent that implements that interface. +In prior versions of FDC3 (<= 2.1) Apps were required to use the 'Desktop Agent Preload' interface, i.e. they relied on the existence of the `window.fdc3` object, which meant that apps running in a standard web browser had to import libraries specific to the Desktop Agent implementation in use. From FDC3 2.2 onwards the 'Desktop Agent Proxy' interface is available, which allows apps in a standard web browser to connect to any Desktop Agent that implements that interface. -Hence, from FDC3 2.2 onwards apps should switch from using `window.fdc3` directly to calling the `getAgent()` function to retrieve a `DesktopAgent` API interface. +Hence, from FDC3 2.2 onwards apps SHOULD call the `getAgent()` to retrieve a `DesktopAgent` API interface. ::: +### getAgent + +//TODO: rewrite this section after updating getAgent docs for using URLs and param for setting window.fdc3. +// Provide a brief introduction and link to getAgent reference + + + :::tip To simplify migration of an app that works with `window.fdc3` to using `getAgent()`, simply set the `fdc3 property on the global object yourself, i.e.: @@ -89,7 +96,7 @@ Applications MAY provide additional fields related to configuration or failover > Note: Applications SHOULD provide visual feedback to users indicating that the app is in the process of connecting. Once the FDC3 interface is accessible the application SHOULD update that visual feedback. -### Failover function +#### Failover function Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. @@ -135,7 +142,7 @@ There are two main ways FDC3 can be used from web applications: #### 1. Direct Usage -Simply rely on the global object being made available by your desktop agent, and address the API directly: +Simply rely on the global object being made available by your Desktop Agent, and address the API directly: ```js function sendData() { @@ -154,7 +161,7 @@ if (window.fdc3) { #### 2. NPM Wrapper -FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can by used by web applications to target operations from the [API Specification](api/spec) in a consistent way. Each FDC3-compliant desktop agent that the application runs in, can then provide an implementation of the FDC3 API operations. +FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can by used by web applications to target operations from the [API Specification](api/spec) in a consistent way. Each FDC3-compliant Desktop Agent that the application runs in, can then provide an implementation of the FDC3 API operations. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -210,7 +217,7 @@ const listener = await addIntentListener('ViewAnalysis', instrument => { The FDC3 Standard does not currently define wire formats for an app to communicate with a Desktop Agent, nor does it define language specific API bindings, other than JavaScript and TypeScript. Hence, for a native application to be FDC3-enabled, it needs to either: -- Make use of a shared library (such as a .NET DLL or JAR file) that provides it with an implementation of the FDC3 API (which ties it to a specific desktop agent implementation). +- Make use of a shared library (such as a .NET DLL or JAR file) that provides it with an implementation of the FDC3 API (which ties it to a specific Desktop Agent implementation). - Model itself as a Desktop Agent (rather than just an app working with one) and use the Agent Bridging protocol to connect to a Desktop Agent Bridge and work through it to interoperate with apps managed by other Desktop Agents. ## Hybrid diff --git a/docs/fdc3-glossary.md b/docs/fdc3-glossary.md index 6fa5a970c..415161b1e 100644 --- a/docs/fdc3-glossary.md +++ b/docs/fdc3-glossary.md @@ -27,6 +27,7 @@ For the purposes of this Standard, the following definitions apply. Other terms - **DAB**: Acronym of Desktop Agent Bridge. - **Desktop Agent**: A desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. The primary interface of the FDC3 API. - **Desktop Agent Bridge**: An independent service that Desktop Agents connect to which allows them to relay requests to other Desktop Agents also connected to the bridge, allowing FDC3-based interop to extend across multiple Desktop Agents and machines. +- **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3` and may be returned by the `getAgent()` function defined in the FDC3 Web Connection Protocol. - **FDC3 API**: A baseline, consistent developer interface for interoperability between applications. - **GUID**: Globally Unique IDentifier, synonymous with UUID. Defined by [IETF RFC4122](references). - **interop**: Shorthand for interoperability. @@ -40,3 +41,4 @@ For the purposes of this Standard, the following definitions apply. Other terms - **resolving an intent**: The act of mapping a specified intent and context object to an application. - **standard intent**: An intent defined by this Standard. - **UUID**: Universally Unique IDentifier, synonymous with GUID. Defined by [IETF RFC4122](references). +- **Web Connection Protocol**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard. From 3cada7f776c99a20f004c90fd40e82d7e1b489f3 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 29 Jul 2024 18:45:59 +0100 Subject: [PATCH 044/152] rework getAgent for using URLs + add glossary entries --- docs/api/ref/GetAgent.md | 242 ++++++++++++++++++++------------ docs/api/supported-platforms.md | 2 +- docs/fdc3-glossary.md | 4 +- 3 files changed, 157 insertions(+), 91 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index c57709519..1cd37b378 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -4,90 +4,124 @@ sidebar_label: GetAgent title: GetAgent --- -The `getAgent()` function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where the FDC3 Web Connection Protocol (WCP) is used to connect back to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. +The `getAgent()` is the recommended way to connect to a Desktop Agent in a web applications. The function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in an environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where a proxy based on the FDC3 Web Connection Protocol (WCP) and Desktop Agent Communication Protocol (DACP) is used to connect to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. -The function accepts a number of arguments that can be used to affect its behavior and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. +The small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. -As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. Hence, applications are expected to provide details of an app directory record to help validate their identity. For more details on identity validation see Web section of the [Supported Platforms page](../supported-platforms#web). +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity =- usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see Web section of the [Supported Platforms page](../supported-platforms#web). If no Desktop Agent is found, or an issue prevent connection to it, the `getAgent()` function will eventually reject its promise, which apps can handle to set themselves up to run without connection to a Desktop Agent or to display an error. ```ts /** - * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports - * injection of the API or by using the FDC3 Web Connection Protocol (WCP) to establish - * a connection to a browser-resident Desktop Agent. + * Function used to retrieve an FDC3 Desktop Agent API instance, which + * supports the discovery of 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). Finally, 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. * - * If a Desktop Agent is not found initially, any `failover` function supplied as an argument - * will be run allowing an app to alternative means of connecting to a Desktop Agent, such - * as the use of a proprietary adaptor, starting on in a new window or iframe, etc. - * - * If no agent is found, via neither the default strategies or any failover function supplied, - * then `getAgent()` will reject with an error from the AgentError enumeration. - * - * @param {GetAgentParams} params Required parameters for the function. + * @param {GetAgentParams} params Optional parameters object, which + * may include a URL to use for the app's identity, other settings + * that affect the behavior of the getAgent() function and a `failover` + * function that should be run if a Desktop Agent is not detected. * - * @return A promise that resolves to an object which contains a DesktopAgent, or which - * rejects with a string from the `AgentError` union if a DesktopAgent cannot be established. + * @returns A promise that resolves to a DesktopAgent implementation or + * rejects with an error message from the `AgentError` enumeration if unable to + * return a Desktop Agent implementation. * * @example - * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp@myorg.com” - * }); - * - * * @example Using appDUrl - * const { desktopAgent: fdc3 } = await getAgent({ - * appDUrl: "https://myorg.com/api/appd/apps/myApp" - * }); - */ - -export type GetAgentFunction = ( - params: GetAgentParams, -) => Promise<{ - desktopAgent: DesktopAgent -}>; + * const fdc3 = await getAgent(); + * // OR + * getAgent({ + * identityUrl: "https://example.com/path?param=appName#example", + * channelSelector: false, + * intentresolver: false + * }).then((fdc3) => { /* do FDC3 stuff */}; + */ +type getAgent = ( + params?: GetAgentParams, +): Promise; + /** * @typedef {Object} GetAgentParams Type representing parameters passed to the - * getAgent function. - * - * @property {string} appId The fully qualified appId that represents the application. - * (in the form @) + * getAgent function. * - * @property {string} appDUrl A URL that points to an appD record for the application. - * Used as an alternative to providing a fully qualified appId. + * @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} timeout Number of milliseconds to allow for establishing a - * DesktopAgent. When the timeout expires, the optional provided failover function will be - * run. Default 750. + * @property {number} timeout Number of millisecs to allow for an fdc3 + * implementation to be found before calling the failover function or + * rejecting (default 1000). 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 {boolean} channelSelector Flag indicating that the application - * requires `getAgent() `to create a channel selector UI, which may be provided - * either by the Desktop Agent or the default FDC3 implementation. Defaults to true. + * needs access to a channel selector UI (i.e. because it supports User Channels + * and does not implement its own UI for selecting channels). If not set will + * default to true. MAY be ignored by Desktop Agent Preload (container) + * implementations. * * @property {boolean} intentResolver Flag indicating that the application - * requires `getAgent()` to create an intent resolver UI, which may be provided - * either by the Desktop Agent or the default FDC3 implementation. Defaults to true. + * needs access to an intent resolver UI (i.e. because it supports raising on or + * more intents and and does not implement its own UI for selecting target apps. + * If not set will default to true. MAY be ignored by Desktop Agent Preload + * (container) implementations. * - * @property {function} failover A optional function that can establish connectivity - * to a DesktopAgent if standard mechanisms fail or otherwise modify the behavior of the - * app when no Desktop Agent is available. If a URL is provided, `getAgent()` will - * create a hidden iframe to load it into. Alternatively, the app's failover function - * may return a `WindowProxy` Object (i.e. the object returned by a `window.open()` - * call or the `contentWindow` property of an iframe). In either case, `getAgent()` - * will re-run its internal algorithm with those objects (restarting the timeout). - * The function may also simply resolve to a DesktopAgent implementation, which will be - * passed along to the app. + * @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 `false` will inhibit that behaviour, leaving `window.fdc3` unset. + * + * @property {function} failover A optional function that provides an + * 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. */ -export type GetAgentParams = { - timeout ?: number, // Defaults to 750 - appId ?: string, - appDUrl ?: string, - channelSelector ?: boolean, - intentResolver ?: boolean, - failover ?: (args: GetAgentParams) => Promise +type GetAgentParams = { + timeout: number = 1000, + identityUrl?: string, + channelSelector: boolean = true, + intentResolver: boolean = true, + dontSetWindowFdc3: boolean = false, + failover?: (args: GetAgentParams) => Promise }; + +/** + * Contains constants representing the errors that can be encountered when + * trying to connect to a web-based Desktop Agent with the getAgent function. + */ +enum AgentError { + /** Returned if no Desktop Agent was found by any means available or + * if the Agent previously connected to is not contactable on a + * subsequent connection attempt.*/ + AgentNotFound = "AgentNotFound", + + /** Returned if validation of the app identity by the Desktop Agent + * Failed or the app is not being allowed to connect to the Desktop Agent + * for another reason. */ + AccessDenied = "AccessDenied", + + /** Returned if an error or exception occurs while trying to set + * up communication with a Desktop Agent. */ + ErrorOnConnect = "ErrorOnConnect", + + /** Returned if either the failover function itself, or what it returned, + * was not the right type. */ + InvalidFailover = "InvalidFailover" +} ``` ## Persisted Connection Data @@ -95,42 +129,72 @@ export type GetAgentParams = { The `getAgent()` function uses SessionStorage ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), [HTML Living Standard](https://html.spec.whatwg.org/multipage/webstorage.html)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. The details persisted conform to the following type: ```ts -/** - * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. - * getAgent() will use this connection information when it exists to ensure a consistent instanceId - * and connection type. - */ -export type DesktopAgentDetails = { - /** The type of DA. Prevents an inadvertent switch to a different agent.*/ +/** Type representing data on the Desktop Agent that an app + * connected to that is persisted by the getAgent function + * to be used when re-connecting (after a navigation or + * refresh event) and to ensure a consistent instanceId. + */ +type DesktopAgentDetails = { + /** The type of Desktop Agent connected to. Used to + * prevent an inadvertent switch to a different agent.*/ agentType: WebDesktopAgentType, - /** May contain the URL that was used to connect to the prior DA. - * This may have been provided by a parent window that has since - * closed or a failover function. It may therefore be used to open - * a new window/iframe and restart the DA. */ - url?: string, + /** The URL that was previously sent to the Desktop Agent + * to establish the app's identity.*/ + identityUrl?: string, + + /** The current URL at the time of the last connection to + * a Desktop Agent.*/ + actualUrl?: string, - /** The prior appId set by this window. If a appDUrl was previously set then - * this appId will be set to the the representative fully qualified appId. */ + /** Optional URL field that should be used to store any + * URL that was used to connect to a Desktop Agent. URLs + * may have been provided by a parent window that has since + * gone away and persisting may allow re-connection in such + * cases. */ + agentUrl?: string, + + /** The appId that was identified for the application by the + * Desktop Agent.*/ appId: string, - /** The instanceId that was issued by the DA to this window. */ + /** The instanceId that was issued to the app by the Desktop + * Agent. */ instanceId: string, - /** The instanceUuid that was previously issued by the DA. - * This MUST be passed when connecting to the DA. The DA will use - * this to determine whether the app has previously connected and - * which instance it was. The DA uses this to reissue the same instanceId. - * - * The instanceUuid is secret. It should never be shared with other applications - * and is not available through the FDC3 API. */ - instanceUuid: string + /** The instanceUuid that was issued to the app. This should be + * passed when connecting to the Desktop Agent to help + * identify that this app has connected before and which + * instance it is, enabling the Desktop Agent to reissue + * the same instanceId. The instanceUuid should never be shared + * with other applications and is not available through the + * FDC3 API, allowing it to be used as a shared secret with + * the Desktop Agent that issued the associated instanceId.*/ + instanceUuid: string } -/** Specifies the means by which a connection to the DA is made. - * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. - * "PARENT" - The DA runs in a parent window or iframe. - * "FAILOVER" - The DA, or details to connect to one, were provided by an app supplied failover function. - */ -export type WebDesktopAgentType = "INJECTED" | "PARENT" | "FAILOVER"; +/** Enumeration of values used to describe types of web-based + * Desktop Agent. Each 'type' refers to the means by which + * a connection to the agent is made and/or an interface to it + * received. */ +enum WebDesktopAgentType = { + /** Denotes Desktop Agents that inject the FDC3 interface + * at `window.fdc3`. */ + PRELOAD = "PRELOAD", + + /** 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`. */ + 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. */ + PROXY_URL = "PROXY_URL", + + /** Denotes a Desktop Agent that was returned by a failover + * function that was passed by the application. */ + FAILOVER = "FAILOVER" +} ``` diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 8853e32f4..2a9f8ae11 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -9,7 +9,7 @@ There are two main categories of platform: web and native, both of which are des ## Web -For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). +For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): diff --git a/docs/fdc3-glossary.md b/docs/fdc3-glossary.md index 415161b1e..b7a328d3b 100644 --- a/docs/fdc3-glossary.md +++ b/docs/fdc3-glossary.md @@ -27,7 +27,9 @@ For the purposes of this Standard, the following definitions apply. Other terms - **DAB**: Acronym of Desktop Agent Bridge. - **Desktop Agent**: A desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. The primary interface of the FDC3 API. - **Desktop Agent Bridge**: An independent service that Desktop Agents connect to which allows them to relay requests to other Desktop Agents also connected to the bridge, allowing FDC3-based interop to extend across multiple Desktop Agents and machines. +- **Desktop Agent Communication Protocol**: JSON communication protocol for the Desktop Agent API. Used by a Desktop Agent Proxy interface over the Channel Messaging API (as specified by the Web Connection Protocol) to communicate with a Desktop Agent running in another window/frame. - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3` and may be returned by the `getAgent()` function defined in the FDC3 Web Connection Protocol. +- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Web Connection Protocol and Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. Returned by the `getAgent()` function provided by the FDC3 NPM module where a browser-based Desktop Agent is detected. - **FDC3 API**: A baseline, consistent developer interface for interoperability between applications. - **GUID**: Globally Unique IDentifier, synonymous with UUID. Defined by [IETF RFC4122](references). - **interop**: Shorthand for interoperability. @@ -41,4 +43,4 @@ For the purposes of this Standard, the following definitions apply. Other terms - **resolving an intent**: The act of mapping a specified intent and context object to an application. - **standard intent**: An intent defined by this Standard. - **UUID**: Universally Unique IDentifier, synonymous with GUID. Defined by [IETF RFC4122](references). -- **Web Connection Protocol**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard. +- **Web Connection Protocol**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard, including both preloaded Desktop Agent interfaces and Browser-based Desktop Agents that work with a Desktop Agent Proxy. From c93c58b935610b0208c4601db74e6b69332d3a63 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 29 Jul 2024 18:51:56 +0100 Subject: [PATCH 045/152] rename 'Browser Communication Protocol' to 'Desktop Agent Communicaton Protocol' --- .../api/specs/browserCommunicationProtocol.md | 80 ------------------- .../api/specs/browserResidentDesktopAgents.md | 4 +- .../desktopAgentCommunicationProtocol.md | 80 +++++++++++++++++++ docs/api/specs/webConnectionProtocol.md | 10 +-- 4 files changed, 87 insertions(+), 87 deletions(-) delete mode 100644 docs/api/specs/browserCommunicationProtocol.md create mode 100644 docs/api/specs/desktopAgentCommunicationProtocol.md diff --git a/docs/api/specs/browserCommunicationProtocol.md b/docs/api/specs/browserCommunicationProtocol.md deleted file mode 100644 index 0d828f62b..000000000 --- a/docs/api/specs/browserCommunicationProtocol.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -id: browserCommunicationProtocol -sidebar_label: Browser Communication Protocol -title: Browser Communication Protocol (next) ---- - -# Browser Communication Protocol (BCP) - -BCP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. - -:::note - -We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. - -::: - -Type definitions for all BCP messages can be found here: [bcp.ts](TODO). - -## Protocol conventions - -The protocol is divided into groups of messages: - -1) Messages sent from the library to the DA. These typically have a 1:1 correspondence with function calls on `DesktopAgent` and `Channel`, and ancillary functionality such as unsubscribing. - -2) Response messages, sent from the DA to the library. Every message sent from the library to the DA will receive a response. In most cases, the type will simply have "Response" appended. For instance, the response message for `getInfo` is `getInfoResponse`. For all other cases the `BCPAck` message will be the response. Every response's payload will contain an error string if an error occurred, otherwise it will contain the expected data. - -3) Asynchronous "inbound" messages, sent from the DA to the library. These messages are due to actions in other apps, such as an inbound context resulting from another app's broadcast. These messages have the name of the originating message appended with `Inbound`. For example, if another app called `broadcast` then this app would receive a message called `broadcastInbound`. - -Every message has a `meta.messageId`. Initiating messages must set this to be a unique string. Response messages must use the string from their corresponding initiating message. The `messageId` can be used by library and DA to match incoming message responses with their initial requests. - -## Multiplexing - -For any given contextType or intent, the library should only ever send `BCPAddContextListener` or `BCPAddIntentListener` one time. The DA is only responsible for sending any given Context or Intent _once_ to an app. The DA may ignore duplicate listener registrations. - -If the app has registered multiple listeners for these types then it is the responsibility of the _library_ to multiplex the delivered Context, or to choose a specific intent listener. - -When the API calls the unsubscriber for a listener then `BCPRemoveContextListener` or `BCPRemoveIntentListener` should be sent to the DA. - -## Intents - -Refer [Private Channel examples](../ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. - -When an app ("client") calls `raiseIntent()` or `raiseIntentByContext()`, the library MUST send the corresponding `BCPRaiseIntent` or `BCPRaiseIntentByContext` message to the DA. - -The DA will resolve the intent and then deliver a `BCPIntentInbound` message to the library in the resolved app ("server"). This message will contain a `responseId` that has been generated by the DA. - -After the message has been sent, the DA will respond back to the "client" app's library with a `BCPRaiseIntentResponse` message containing that `responseId`. - -If the "client" app then calls `getResult()`, then the library will wait until a `BCPIntentResult` message is received with a corresponding `responseId`. It will resolve the `getResult()` call with either a Context or PrivateChannel depending on the contents of the result. - -Meanwhile, if the "server" app's intent handler resolves to a Channel or Context then the library should send a `BCPIntentResult` with the `responseId` that was initially received from `BCPIntentInbound`. - - -## Intent Resolver - -The DA should send `BCPResolveIntent` if it requires an external UI for intent resolution. This MUST include the list of available apps which are capable of being launched to handle the intent, and it MUST include the list of open apps which are capable of handling the intent. The "@finos/fdc3" library will present UI to the end user, and then will respond with a `BCPResolveIntentResponse` containing the user's choice. - -DAs are free to provide their own intent resolution UIs if they have this capability. - -## Channels - -The DA should send `BCPInitializeChannelSelector` if it requires the app to provide UI for channel selection. The "@finos/fdc3" library will provide the UI when this message is received. - -Any message related to a channel contains a `channelId` field. It is the responsibility of each party (DA and library) to correlate `channelId` fields with the correct local objects. - -For instance, when the library receives a `BCPBroadcastInbound` message, it should look for the `channelId` field, and only deliver that message to listeners on the corresponding local `Channel` object. - -Likewise, when an app calls `channel.broadcast()` then the library should send the `BCPBroadcast` message with the `channelId` set accordingly. - -## Private Channels - -In general, private channels behave as channels. The DA MUST assign a unique `channelId` in response to `BCPCreatePrivateChannel` messages. `BCPBroadcast` and `BCPAddContextListener` messages can be transmitted with this `channelId`. - -See [Intents](#intents) for the process that is used to established a private channel. - -FDC3's `PrivateChannel` object has some specific functions, each of which has a corresponding BCP message. For instance, `PrivateChannel.onAddContextListener()` can be implemented using the `BCPPrivateChannelOnAddContextListener` message. Each of these types of messages contains a `channelId` which can be used to identify the channel. - -The DA should send `BCPPrivateChannelOnAddContextListener` and `BCPPrivateChannelOnUnsubscribe` messages whenever `BCPAddContextListener` or `BCPRemoveContextListener` is called on a private channel. These will be delivered to the library regardless of whether a client has actually called `onAddContextListener()` and `onUnsubscribe()`. It is the library's responsibility to track these calls and either deliver or discard the messages accordingly. - -Likewise, the DA should send `BCPPrivateChannelOnDisconnect` whenever the `BCPPrivateChannelDisconnect` message is received. It is the library's responsibility to deliver or discard this message. diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 1174310e3..83e7eb231 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -72,7 +72,7 @@ window.addEventListener("message", (event) => { }); ``` -## Responding to app communications with Browser Communication Protocol (BCP) +## Responding to app communications with Desktop Agent Communication Protocol (DACP) BCP processing should begin by setting up an inactive connection instance. This instance will become active after the first BCP "WCPValidateAppIdentity" message is received and processed (or deleted if it fails). It is important to remember the WindowProxy (event.source). @@ -207,7 +207,7 @@ const authenticateApp = (connection, e) => { Each message should be responded to with its corresponding response when a response should contain data, or with `BCPAck` if only an acknowledgement is required. -See [Browser Communication Protocol](./browserCommunicationProtocol.md) +See [Desktop Agent Communication Protocol ](./browserCommunicationProtocol.md) See bcp.ts for a full list of BCP messages. diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md new file mode 100644 index 000000000..f5000dea0 --- /dev/null +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -0,0 +1,80 @@ +--- +id: desktopAgentCommunicationProtocol +sidebar_label: Desktop Agent Communication Protocol +title: Desktop Agent Communication Protocol (next) +--- + +# Desktop Agent Communication Protocol (DACP) + +DACP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. + +:::note + +We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. + +::: + +Type definitions for all DACP messages can be found here: [bcp.ts](TODO). + +## Protocol conventions + +The protocol is divided into groups of messages: + +1) Messages sent from the library to the DA. These typically have a 1:1 correspondence with function calls on `DesktopAgent` and `Channel`, and ancillary functionality such as unsubscribing. + +2) Response messages, sent from the DA to the library. Every message sent from the library to the DA will receive a response. In most cases, the type will simply have "Response" appended. For instance, the response message for `getInfo` is `getInfoResponse`. For all other cases the `DACPAck` message will be the response. Every response's payload will contain an error string if an error occurred, otherwise it will contain the expected data. + +3) Asynchronous "inbound" messages, sent from the DA to the library. These messages are due to actions in other apps, such as an inbound context resulting from another app's broadcast. These messages have the name of the originating message appended with `Inbound`. For example, if another app called `broadcast` then this app would receive a message called `broadcastInbound`. + +Every message has a `meta.messageId`. Initiating messages must set this to be a unique string. Response messages must use the string from their corresponding initiating message. The `messageId` can be used by library and DA to match incoming message responses with their initial requests. + +## Multiplexing + +For any given contextType or intent, the library should only ever send `DACPAddContextListener` or `DACPAddIntentListener` one time. The DA is only responsible for sending any given Context or Intent _once_ to an app. The DA may ignore duplicate listener registrations. + +If the app has registered multiple listeners for these types then it is the responsibility of the _library_ to multiplex the delivered Context, or to choose a specific intent listener. + +When the API calls the unsubscriber for a listener then `DACPRemoveContextListener` or `DACPRemoveIntentListener` should be sent to the DA. + +## Intents + +Refer [Private Channel examples](../ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. + +When an app ("client") calls `raiseIntent()` or `raiseIntentByContext()`, the library MUST send the corresponding `DACPRaiseIntent` or `DACPRaiseIntentByContext` message to the DA. + +The DA will resolve the intent and then deliver a `DACPIntentInbound` message to the library in the resolved app ("server"). This message will contain a `responseId` that has been generated by the DA. + +After the message has been sent, the DA will respond back to the "client" app's library with a `DACPRaiseIntentResponse` message containing that `responseId`. + +If the "client" app then calls `getResult()`, then the library will wait until a `DACPIntentResult` message is received with a corresponding `responseId`. It will resolve the `getResult()` call with either a Context or PrivateChannel depending on the contents of the result. + +Meanwhile, if the "server" app's intent handler resolves to a Channel or Context then the library should send a `DACPIntentResult` with the `responseId` that was initially received from `DACPIntentInbound`. + + +## Intent Resolver + +The DA should send `DACPResolveIntent` if it requires an external UI for intent resolution. This MUST include the list of available apps which are capable of being launched to handle the intent, and it MUST include the list of open apps which are capable of handling the intent. The "@finos/fdc3" library will present UI to the end user, and then will respond with a `DACPResolveIntentResponse` containing the user's choice. + +DAs are free to provide their own intent resolution UIs if they have this capability. + +## Channels + +The DA should send `DACPInitializeChannelSelector` if it requires the app to provide UI for channel selection. The "@finos/fdc3" library will provide the UI when this message is received. + +Any message related to a channel contains a `channelId` field. It is the responsibility of each party (DA and library) to correlate `channelId` fields with the correct local objects. + +For instance, when the library receives a `DACPBroadcastInbound` message, it should look for the `channelId` field, and only deliver that message to listeners on the corresponding local `Channel` object. + +Likewise, when an app calls `channel.broadcast()` then the library should send the `DACPBroadcast` message with the `channelId` set accordingly. + +## Private Channels + +In general, private channels behave as channels. The DA MUST assign a unique `channelId` in response to `DACPCreatePrivateChannel` messages. `DACPBroadcast` and `DACPAddContextListener` messages can be transmitted with this `channelId`. + +See [Intents](#intents) for the process that is used to established a private channel. + +FDC3's `PrivateChannel` object has some specific functions, each of which has a corresponding DACP message. For instance, `PrivateChannel.onAddContextListener()` can be implemented using the `DACPPrivateChannelOnAddContextListener` message. Each of these types of messages contains a `channelId` which can be used to identify the channel. + +The DA should send `DACPPrivateChannelOnAddContextListener` and `DACPPrivateChannelOnUnsubscribe` messages whenever `DACPAddContextListener` or `DACPRemoveContextListener` is called on a private channel. These will be delivered to the library regardless of whether a client has actually called `onAddContextListener()` and `onUnsubscribe()`. It is the library's responsibility to track these calls and either deliver or discard the messages accordingly. + +Likewise, the DA should send `DACPPrivateChannelOnDisconnect` whenever the `DACPPrivateChannelDisconnect` message is received. It is the library's responsibility to deliver or discard this message. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 0930b15e6..c026d359d 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -12,11 +12,11 @@ title: FDC3 Web Connection Protocol (next) > Note - Throughout this document, when referring to "DA" without any other qualification we mean a Browser-Resident Desktop Agent. -**getAgent()**: The library function provided by `@finos/fdc3` that discovers and establishes communication to DAs. It may (1) return a reference to an injected `DesktopAgent` instance, (2) use the FDC3 Web Connection Protocol (WCP) to discover a DA (e.g. in a "parent" window or frame) and return a `DesktopAgent` instance that communicates with the DA using the FDC3 Browser Communication Protocol (BCP), or (3) run an application provided failover function that provides direct or indirect access to a `DesktopAgent`. +**getAgent()**: The library function provided by `@finos/fdc3` that discovers and establishes communication to DAs. It may (1) return a reference to an injected `DesktopAgent` instance, (2) use the FDC3 Web Connection Protocol (WCP) to discover a DA (e.g. in a "parent" window or frame) and return a `DesktopAgent` instance that communicates with the DA using the FDC3 Desktop Agent Communication Protocol (DACP), or (3) run an application provided failover function that provides direct or indirect access to a `DesktopAgent`. **Web Connection Protocol (WCP)**: A protocol for discovering and establishing communications with a DA. Ths includes a prescribed algorithm as well as some standard messages that are transmitted using `window.postMessage`. -**Browser Communication Protocol (BCP)**: A protocol that uses the standard HTML Channel Messaging API (MessagePort) to communicate with a DA in a remote iframe or window via messages. +**Desktop Agent Communication Protocol (DACP)**: A protocol that uses the standard HTML Channel Messaging API (MessagePort) to communicate with a DA in a remote iframe or window via messages. **Parent**: A browser window or frame that creates the window or iframe in which an application runs. (The parent provides a `WindowProxy` object which is used by WCP.) @@ -135,13 +135,13 @@ Once a connection is established, the `DesktopAgentDetails` record that was retu Resolve the `getAgent()` promise with an object containing the `DesktopAgent` from step 1, and `ImplementationMetadata` and `AppMetadata` which were provided by the response from step 2. -## Communicating using the Browser Communication Protocol (BCP) +## Communicating using the Desktop Agent Communication Protocol (DACP) -The `DesktopAgent` instantiated by calls to `getAgent()` uses the Browser Communication Protocol (BCP) to interact with the DA that is located in another frame. This protocol is based on exchanges of a standardized set of Flux Standard Actions (FSAs). The protocol is bi-directional. Every FSA that is transmitted results in a corresponding FSA in return, either containing requested data or simply acknowledging receipt. +The `DesktopAgent` instantiated by calls to `getAgent()` uses the Desktop Agent Communication Protocol (DACP) to interact with the DA that is located in another frame. This protocol is based on exchanges of a standardized set of Flux Standard Actions (FSAs). The protocol is bi-directional. Every FSA that is transmitted results in a corresponding FSA in return, either containing requested data or simply acknowledging receipt. BCP uses the HTML Channel Messaging API, communicating via the `MessagePort` object that was established by the Web Connection Protocol (WCP) covered above. -See [Browser Communication Protocol](./browserCommunicationProtocol.md) +See [Desktop Agent Communication Protocol ](./browserCommunicationProtocol.md) ## Built in UI (Channel Selector and Intent Resolver) From 77f00232daffb39cd39ac88483ae3b9dda3b8eb1 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Jul 2024 10:43:47 +0100 Subject: [PATCH 046/152] Adding addEventListener messages --- docs/api/supported-platforms.md | 1 - schemas/api/addEventListenerEvent.schema.json | 44 +++ .../api/addEventListenerRequest.schema.json | 46 +++ .../api/addEventListenerResponse.schema.json | 64 ++++ schemas/api/agentEvent.schema.json | 3 +- schemas/api/agentResponse.schema.json | 1 + schemas/api/api.schema.json | 26 ++ schemas/api/appRequest.schema.json | 1 + src/api/BrowserTypes.ts | 320 ++++++++++++++---- 9 files changed, 444 insertions(+), 62 deletions(-) create mode 100644 schemas/api/addEventListenerEvent.schema.json create mode 100644 schemas/api/addEventListenerRequest.schema.json create mode 100644 schemas/api/addEventListenerResponse.schema.json diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 2a9f8ae11..ff591d5ed 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -34,7 +34,6 @@ Hence, from FDC3 2.2 onwards apps SHOULD call the `getAgent()` to retrieve a `De // Provide a brief introduction and link to getAgent reference - :::tip To simplify migration of an app that works with `window.fdc3` to using `getAgent()`, simply set the `fdc3 property on the global object yourself, i.e.: diff --git a/schemas/api/addEventListenerEvent.schema.json b/schemas/api/addEventListenerEvent.schema.json new file mode 100644 index 000000000..5807b54c8 --- /dev/null +++ b/schemas/api/addEventListenerEvent.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addEventListenerEvent.schema.json", + "type": "object", + "title": "addEventListener Event", + "description": "An event message from the Desktop Agent to an app for a specified event type.", + "allOf": [ + { + "$ref": "agentEvent.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddEventListenerEventType" + }, + "payload": { + "$ref": "#/$defs/AddEventListenerEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddEventListenerEventType": { + "title": "AddEventListener Event Message Type", + "const": "addEventListenerEvent" + }, + "AddEventListenerEventPayload": { + "title": "addEventListener Event Payload", + "type": "object", + "properties": { + "event": { + "$ref": "api.schema.json#/definitions/FDC3Event" + } + }, + "additionalProperties": false, + "required": [ + "event" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/addEventListenerRequest.schema.json b/schemas/api/addEventListenerRequest.schema.json new file mode 100644 index 000000000..9a05aeef2 --- /dev/null +++ b/schemas/api/addEventListenerRequest.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addEventListenerRequest.schema.json", + "type": "object", + "title": "AddEventListener Request", + "description": "A request to add an event listener for a specified event type to the DesktopAgent.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddEventListenerRequestType" + }, + "payload": { + "$ref": "#/$defs/AddEventListenerRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddEventListenerRequestType": { + "title": "AddEventListener Request Message Type", + "const": "addEventListenerRequest" + }, + "AddEventListenerRequestPayload": { + "title": "AddEventListener Request Payload", + "type": "object", + "properties": { + "type": { + "title": "Event type", + "description": "The type of the event to be listened to.", + "$ref": "api.schema.json#/definitions/FDC3EventType" + } + }, + "additionalProperties": false, + "required": [ + "type" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/addEventListenerResponse.schema.json b/schemas/api/addEventListenerResponse.schema.json new file mode 100644 index 000000000..f7a853635 --- /dev/null +++ b/schemas/api/addEventListenerResponse.schema.json @@ -0,0 +1,64 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/addEventListenerResponse.schema.json", + "type": "object", + "title": "AddEventListener Response", + "description": "A response to an addEventListener request.", + "allOf": [ + { + "$ref": "agentResponse.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/AddEventListenerResponseType" + }, + "payload": { + "oneOf": [ + { + "$ref": "#/$defs/AddEventListenerSuccessResponsePayload" + }, + { + "$ref": "#/$defs/AddEventListenerErrorResponsePayload" + } + ] + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "AddEventListenerResponseType": { + "title": "AddEventListener Response Message Type", + "const": "addEventListenerResponse" + }, + "AddEventListenerSuccessResponsePayload": { + "title": "AddEventListener Response Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ], + "additionalProperties": false + }, + "AddEventListenerErrorResponsePayload": { + "title": "AddEventListener Error Response Payload", + "type": "object", + "properties": { + "error": { + "$ref": "common.schema.json#/$defs/ErrorMessages" + } + }, + "required": [ + "error" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/agentEvent.schema.json b/schemas/api/agentEvent.schema.json index d36797b7b..9c7b46bdd 100644 --- a/schemas/api/agentEvent.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -14,7 +14,8 @@ "channelChangedEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", - "privateChannelOnUnsubscribeEvent" + "privateChannelOnUnsubscribeEvent", + "addEventListenerEvent" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." }, diff --git a/schemas/api/agentResponse.schema.json b/schemas/api/agentResponse.schema.json index 8eab915bf..14101c318 100644 --- a/schemas/api/agentResponse.schema.json +++ b/schemas/api/agentResponse.schema.json @@ -10,6 +10,7 @@ "type": "string", "enum": [ "addContextListenerResponse", + "addEventListenerResponse", "addIntentListenerResponse", "broadcastResponse", "contextListenerUnsubscribeResponse", diff --git a/schemas/api/api.schema.json b/schemas/api/api.schema.json index 4265b3b5a..def36e745 100644 --- a/schemas/api/api.schema.json +++ b/schemas/api/api.schema.json @@ -467,6 +467,32 @@ "intent", "source" ] + }, + "FDC3EventType": { + "title": "FDC3 Event Type", + "description": "The type of a (non-context and non-intent) event that may be received via the FDC3 API's addEventListener function.", + "type": "string", + "enum": [ + "USER_CHANNEL_CHANGED" + ] + }, + "FDC3Event": { + "title": "FDC3 Event", + "description": "An event object received via the FDC3 API's addEventListener function. Will always include both type and details, which describe type of the event and any additional details respectively.", + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/FDC3EventType" + }, + "details": { + "title": "Event details", + "description": "Additional details of the event, such as the `currentChannelId` for a CHANNEL_CHANGED event", + "type": "object", + "additionalProperties": true + } + }, + "required":["type","details"], + "additionalProperties": false } } } \ No newline at end of file diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index d8a519ba1..fd2f30646 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -10,6 +10,7 @@ "type": "string", "enum": [ "addContextListenerRequest", + "addEventListenerRequest", "addIntentListenerRequest", "broadcastRequest", "contextListenerUnsubscribeRequest", diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 24eb0b8c6..87e22ffc4 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,9 +1,12 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); +// const addEventListenerEvent = Convert.toAddEventListenerEvent(json); +// const addEventListenerRequest = Convert.toAddEventListenerRequest(json); +// const addEventListenerResponse = Convert.toAddEventListenerResponse(json); // const addIntentListenerRequest = Convert.toAddIntentListenerRequest(json); // const addIntentListenerResponse = Convert.toAddIntentListenerResponse(json); // const agentEventMessage = Convert.toAgentEventMessage(json); @@ -241,6 +244,153 @@ export interface AddContextListenerResponsePayload { */ export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound"; +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * An event message from the Desktop Agent to an app for a specified event type. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface AddEventListenerEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: AddEventListenerEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: AddEventListenerEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "addEventListenerEvent"; +} + +/** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ +export interface AddEventListenerEventMeta { + eventUuid: string; + timestamp: Date; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface AddEventListenerEventPayload { + event: FDC3Event; +} + +/** + * An event object received via the FDC3 API's addEventListener function. Will always + * include both type and details, which describe type of the event and any additional + * details respectively. + */ +export interface FDC3Event { + /** + * Additional details of the event, such as the `currentChannelId` for a CHANNEL_CHANGED + * event + */ + details: { [key: string]: any }; + type: "USER_CHANNEL_CHANGED"; +} + +/** + * The type of a (non-context and non-intent) event that may be received via the FDC3 API's + * addEventListener function. + * + * The type of the event to be listened to. + */ + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to add an event listener for a specified event type to the DesktopAgent. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface AddEventListenerRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: AddEventListenerRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "addEventListenerRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface AddEventListenerRequestPayload { + /** + * The type of the event to be listened to. + */ + type: "USER_CHANNEL_CHANGED"; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to an addEventListener request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface AddEventListenerResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: AddEventListenerResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "addEventListenerResponse"; +} + +/** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ +export interface AddEventListenerResponsePayload { + error?: ResponsePayloadError; + listenerUUID?: string; +} + +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + */ +export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "IntentHandlerRejected" | "NoResultReturned" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -363,7 +513,7 @@ export interface AgentEventMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; +export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent" | "addEventListenerEvent"; /** * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the @@ -411,21 +561,11 @@ export interface AgentResponseMessageResponsePayload { [property: string]: any; } -/** - * Constants representing the errors that can be encountered when calling the `open` method - * on the DesktopAgent object (`fdc3`). - * - * Constants representing the errors that can be encountered when calling the `findIntent`, - * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the - * DesktopAgent (`fdc3`). - */ -export type ResponsePayloadError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound" | "AppNotFound" | "AppTimeout" | "DesktopAgentNotFound" | "ErrorOnLaunch" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "IntentHandlerRejected" | "NoResultReturned" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; - /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type ResponseMessageType = "addContextListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; +export type ResponseMessageType = "addContextListenerResponse" | "addEventListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; /** * A request message from an FDC3-enabled app to a Desktop Agent. @@ -465,7 +605,7 @@ export interface AppRequestMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -477,7 +617,7 @@ export interface BroadcastEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -489,14 +629,6 @@ export interface BroadcastEvent { type: "broadcastEvent"; } -/** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ -export interface BroadcastEventMeta { - eventUuid: string; - timestamp: Date; -} - /** * The message payload contains details of the event that the app is being notified about. */ @@ -673,7 +805,7 @@ export interface ChannelChangedEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2159,7 +2291,7 @@ export interface IntentEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2660,7 +2792,7 @@ export interface PrivateChannelOnAddContextListenerEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2702,7 +2834,7 @@ export interface PrivateChannelOnDisconnectEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2739,7 +2871,7 @@ export interface PrivateChannelOnUnsubscribeEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: BroadcastEventMeta; + meta: AddEventListenerEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -3377,6 +3509,30 @@ export class Convert { return JSON.stringify(uncast(value, r("AddContextListenerResponse")), null, 2); } + public static toAddEventListenerEvent(json: string): AddEventListenerEvent { + return cast(JSON.parse(json), r("AddEventListenerEvent")); + } + + public static addEventListenerEventToJson(value: AddEventListenerEvent): string { + return JSON.stringify(uncast(value, r("AddEventListenerEvent")), null, 2); + } + + public static toAddEventListenerRequest(json: string): AddEventListenerRequest { + return cast(JSON.parse(json), r("AddEventListenerRequest")); + } + + public static addEventListenerRequestToJson(value: AddEventListenerRequest): string { + return JSON.stringify(uncast(value, r("AddEventListenerRequest")), null, 2); + } + + public static toAddEventListenerResponse(json: string): AddEventListenerResponse { + return cast(JSON.parse(json), r("AddEventListenerResponse")); + } + + public static addEventListenerResponseToJson(value: AddEventListenerResponse): string { + return JSON.stringify(uncast(value, r("AddEventListenerResponse")), null, 2); + } + public static toAddIntentListenerRequest(json: string): AddIntentListenerRequest { return cast(JSON.parse(json), r("AddIntentListenerRequest")); } @@ -4125,6 +4281,39 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, ], false), + "AddEventListenerEvent": o([ + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "payload", js: "payload", typ: r("AddEventListenerEventPayload") }, + { json: "type", js: "type", typ: r("AddEventListenerEventType") }, + ], false), + "AddEventListenerEventMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "AddEventListenerEventPayload": o([ + { json: "event", js: "event", typ: r("FDC3Event") }, + ], false), + "FDC3Event": o([ + { json: "details", js: "details", typ: m("any") }, + { json: "type", js: "type", typ: r("FDC3EventType") }, + ], false), + "AddEventListenerRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("AddEventListenerRequestPayload") }, + { json: "type", js: "type", typ: r("AddEventListenerRequestType") }, + ], false), + "AddEventListenerRequestPayload": o([ + { json: "type", js: "type", typ: r("FDC3EventType") }, + ], false), + "AddEventListenerResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("AddEventListenerResponsePayload") }, + { json: "type", js: "type", typ: r("AddEventListenerResponseType") }, + ], false), + "AddEventListenerResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, + { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, + ], false), "AddIntentListenerRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("AddIntentListenerRequestPayload") }, @@ -4176,14 +4365,10 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "BroadcastEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("BroadcastEventPayload") }, { json: "type", js: "type", typ: r("BroadcastEventType") }, ], false), - "BroadcastEventMeta": o([ - { json: "eventUuid", js: "eventUuid", typ: "" }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "BroadcastEventPayload": o([ { json: "channelId", js: "channelId", typ: "" }, { json: "context", js: "context", typ: r("Context") }, @@ -4212,7 +4397,7 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, ], "any"), "ChannelChangedEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("ChannelChangedEventPayload") }, { json: "type", js: "type", typ: r("ChannelChangedEventType") }, ], false), @@ -4536,7 +4721,7 @@ const typeMap: any = { { json: "intent", js: "intent", typ: u(undefined, "") }, ], false), "IntentEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("IntentEventPayload") }, { json: "type", js: "type", typ: r("IntentEventType") }, ], false), @@ -4642,7 +4827,7 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, ], false), "PrivateChannelOnAddContextListenerEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnAddContextListenerEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnAddContextListenerEventType") }, ], false), @@ -4651,7 +4836,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnDisconnectEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnDisconnectEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnDisconnectEventType") }, ], false), @@ -4659,7 +4844,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnUnsubscribeEvent": o([ - { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventType") }, ], false), @@ -4815,30 +5000,14 @@ const typeMap: any = { "AddContextListenerResponseType": [ "addContextListenerResponse", ], - "AddIntentListenerRequestType": [ - "addIntentListenerRequest", - ], - "FluffyError": [ - "DesktopAgentNotFound", - "IntentDeliveryFailed", - "MalformedContext", - "NoAppsFound", - "ResolverTimeout", - "ResolverUnavailable", - "TargetAppUnavailable", - "TargetInstanceUnavailable", - "UserCancelledResolution", + "FDC3EventType": [ + "USER_CHANNEL_CHANGED", ], - "AddIntentListenerResponseType": [ - "addIntentListenerResponse", + "AddEventListenerEventType": [ + "addEventListenerEvent", ], - "EventMessageType": [ - "broadcastEvent", - "channelChangedEvent", - "intentEvent", - "privateChannelOnAddContextListenerEvent", - "privateChannelOnDisconnectEvent", - "privateChannelOnUnsubscribeEvent", + "AddEventListenerRequestType": [ + "addEventListenerRequest", ], "ResponsePayloadError": [ "AccessDenied", @@ -4863,8 +5032,38 @@ const typeMap: any = { "TargetInstanceUnavailable", "UserCancelledResolution", ], + "AddEventListenerResponseType": [ + "addEventListenerResponse", + ], + "AddIntentListenerRequestType": [ + "addIntentListenerRequest", + ], + "FluffyError": [ + "DesktopAgentNotFound", + "IntentDeliveryFailed", + "MalformedContext", + "NoAppsFound", + "ResolverTimeout", + "ResolverUnavailable", + "TargetAppUnavailable", + "TargetInstanceUnavailable", + "UserCancelledResolution", + ], + "AddIntentListenerResponseType": [ + "addIntentListenerResponse", + ], + "EventMessageType": [ + "addEventListenerEvent", + "broadcastEvent", + "channelChangedEvent", + "intentEvent", + "privateChannelOnAddContextListenerEvent", + "privateChannelOnDisconnectEvent", + "privateChannelOnUnsubscribeEvent", + ], "ResponseMessageType": [ "addContextListenerResponse", + "addEventListenerResponse", "addIntentListenerResponse", "broadcastResponse", "contextListenerUnsubscribeResponse", @@ -4891,6 +5090,7 @@ const typeMap: any = { ], "RequestMessageType": [ "addContextListenerRequest", + "addEventListenerRequest", "addIntentListenerRequest", "broadcastRequest", "contextListenerUnsubscribeRequest", From 689038e171fe130547f82ec78a6d066b6ee86d79 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Jul 2024 17:37:04 +0100 Subject: [PATCH 047/152] Added notes on retrieval of current context when adding context listeners or joining channels --- .../api/addContextListenerRequest.schema.json | 2 +- .../api/addContextListenerResponse.schema.json | 2 +- schemas/api/joinUserChannelRequest.schema.json | 2 +- .../api/joinUserChannelResponse.schema.json | 2 +- src/api/BrowserTypes.ts | 18 ++++++++++++++---- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/schemas/api/addContextListenerRequest.schema.json b/schemas/api/addContextListenerRequest.schema.json index a001edb53..dfbc03ad5 100644 --- a/schemas/api/addContextListenerRequest.schema.json +++ b/schemas/api/addContextListenerRequest.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/addContextListenerRequest.schema.json", "type": "object", "title": "AddContextListener Request", - "description": "A request to add a context listener to a specified Channel OR to the current user channel.", + "description": "A request to add a context listener to a specified Channel OR to the current user channel. Where the listener is added to the current user channel (channelId == null), and this app has already been added to a user channel, client code should make a subsequent request to get the current context of that channel for this listener and then call its handler with it.", "allOf": [ { "$ref": "appRequest.schema.json" diff --git a/schemas/api/addContextListenerResponse.schema.json b/schemas/api/addContextListenerResponse.schema.json index d4addabc1..bd70fd066 100644 --- a/schemas/api/addContextListenerResponse.schema.json +++ b/schemas/api/addContextListenerResponse.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/addContextListenerResponse.schema.json", "type": "object", "title": "AddContextListener Response", - "description": "A response to a addContextListener request.", + "description": "A response to a addContextListener request. Where the listener was added to the current user channel (channelId == null), and this app has already been added to a user channel, client code should make a subsequent request to get the current context of that channel for this listener and then call its handler with it.", "allOf": [ { "$ref": "agentResponse.schema.json" diff --git a/schemas/api/joinUserChannelRequest.schema.json b/schemas/api/joinUserChannelRequest.schema.json index 1edd43ac6..12f6a9249 100644 --- a/schemas/api/joinUserChannelRequest.schema.json +++ b/schemas/api/joinUserChannelRequest.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/joinUserChannelRequest.schema.json", "type": "object", "title": "JoinUserChannel Request", - "description": "Request to join the app to the specified User channel.", + "description": "Request to join the app to the specified User channel. On successfully joining a channel, client code should make subsequent requests to get the current context of that channel for all registered context listeners and then call their handlers with it.", "allOf": [ { "$ref": "appRequest.schema.json" diff --git a/schemas/api/joinUserChannelResponse.schema.json b/schemas/api/joinUserChannelResponse.schema.json index 5f49ad9e8..fe6aeedcb 100644 --- a/schemas/api/joinUserChannelResponse.schema.json +++ b/schemas/api/joinUserChannelResponse.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/joinUserChannelResponse.schema.json", "type": "object", "title": "JoinUserChannel Response", - "description": "A response to a joinUserChannel request.", + "description": "A response to a joinUserChannel request. On receipt of this response, client code should make subsequent requests to get the current context of that channel for all registered context listeners and then call their handlers with it.", "allOf": [ { "$ref": "agentResponse.schema.json" diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 87e22ffc4..ced5a0e59 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -83,7 +83,10 @@ /** * A request to add a context listener to a specified Channel OR to the current user - * channel. + * channel. Where the listener is added to the current user channel (channelId == null), and + * this app has already been added to a user channel, client code should make a subsequent + * request to get the current context of that channel for this listener and then call its + * handler with it. * * A request message from an FDC3-enabled app to a Desktop Agent. */ @@ -187,7 +190,10 @@ export interface AddContextListenerRequestPayload { */ /** - * A response to a addContextListener request. + * A response to a addContextListener request. Where the listener was added to the current + * user channel (channelId == null), and this app has already been added to a user channel, + * client code should make a subsequent request to get the current context of that channel + * for this listener and then call its handler with it. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. @@ -2389,7 +2395,9 @@ export interface IntentListenerUnsubscribeResponse { */ /** - * Request to join the app to the specified User channel. + * Request to join the app to the specified User channel. On successfully joining a channel, + * client code should make subsequent requests to get the current context of that channel + * for all registered context listeners and then call their handlers with it. * * A request message from an FDC3-enabled app to a Desktop Agent. */ @@ -2425,7 +2433,9 @@ export interface JoinUserChannelRequestPayload { */ /** - * A response to a joinUserChannel request. + * A response to a joinUserChannel request. On receipt of this response, client code should + * make subsequent requests to get the current context of that channel for all registered + * context listeners and then call their handlers with it. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. From 3465739331289d72038c5d0e8a2f2b443c27f16a Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 30 Jul 2024 21:03:48 +0100 Subject: [PATCH 048/152] fix docusaurus sidebar + edits to getAgent, supported platforms, schemas Readme --- docs/api/ref/GetAgent.md | 47 ++++++++++++-- .../api/specs/browserResidentDesktopAgents.md | 2 +- docs/api/specs/webConnectionProtocol.md | 2 +- docs/api/supported-platforms.md | 61 +++++-------------- schemas/api/README.md | 14 ++++- website/sidebars.json | 2 +- 6 files changed, 73 insertions(+), 55 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 1cd37b378..f32ba614e 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -4,14 +4,13 @@ sidebar_label: GetAgent title: GetAgent --- -The `getAgent()` is the recommended way to connect to a Desktop Agent in a web applications. The function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in an environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where a proxy based on the FDC3 Web Connection Protocol (WCP) and Desktop Agent Communication Protocol (DACP) is used to connect to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. +The `getAgent()` is the recommended way to connect to a Desktop Agent in a web applications. The function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in an environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where a proxy based on the [FDC3 Web Connection Protocol (WCP)](../specs/webConnectionProtocol) and [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) is used to connect to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. The small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. -As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity =- usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see Web section of the [Supported Platforms page](../supported-platforms#web). +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). -If no Desktop Agent is found, or an issue prevent connection to it, the `getAgent()` function will eventually reject -its promise, which apps can handle to set themselves up to run without connection to a Desktop Agent or to display an error. +If no Desktop Agent is found, or an issue prevent connection to it, the `getAgent()` function will eventually reject its promise, which apps can handle to set themselves up to run without connection to a Desktop Agent or to display an error. ```ts /** @@ -124,9 +123,47 @@ enum AgentError { } ``` +## Failover function + +Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. + +Example: Decreasing the timeout and providing a failover function + +```js + const fdc3 = await getAgent({ + appId: “myApp@yourorg.org”, + timeout: 250, + failover: async (params) => { + // return WindowProxy | URL | DesktopAgent + } + }); +``` + +The failover function allows an application to provide a backup mechanism for connecting to a DA. It is called only when establishment through normal procedures fails or times out. + +> Note - Failover can occur quicker than the timeout. For instance when an end user opens an FDC3 app in a new browser tab it will immediately failover because there will be no injected DesktopAgent and there will be no parent or opener windows. + +> Note - A second timeout is started when the failover function is called. So the total possible elapsed time to establish a connection is 2X the established timeout when a failover function is provided. + +> 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. + +Failover functions MUST be asynchronous MUST resolve to one of the following types: + +1) DesktopAgent + The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. +2) WindowProxy (Window object) + The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. +3) URL + If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. + + If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. + ## Persisted Connection Data -The `getAgent()` function uses SessionStorage ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), [HTML Living Standard](https://html.spec.whatwg.org/multipage/webstorage.html)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. The details persisted conform to the following type: +The `getAgent()` function uses SessionStorage ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), [HTML Living Standard](https://html.spec.whatwg.org/multipage/webstorage.html)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. Applications are not expected to interact with this information directly, rather it is set and used by the `getAgent()` implementation. + + +The details persisted conform to the following type: ```ts /** Type representing data on the Desktop Agent that an app diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 83e7eb231..7bd8f78cb 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -207,7 +207,7 @@ const authenticateApp = (connection, e) => { Each message should be responded to with its corresponding response when a response should contain data, or with `BCPAck` if only an acknowledgement is required. -See [Desktop Agent Communication Protocol ](./browserCommunicationProtocol.md) +See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) See bcp.ts for a full list of BCP messages. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index c026d359d..84558535a 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -141,7 +141,7 @@ The `DesktopAgent` instantiated by calls to `getAgent()` uses the Desktop Agent BCP uses the HTML Channel Messaging API, communicating via the `MessagePort` object that was established by the Web Connection Protocol (WCP) covered above. -See [Desktop Agent Communication Protocol ](./browserCommunicationProtocol.md) +See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) ## Built in UI (Channel Selector and Intent Resolver) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index ff591d5ed..1f9faf20f 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -9,6 +9,20 @@ There are two main categories of platform: web and native, both of which are des ## Web +:::tip + +The recommended way to get access to the FDC3 Desktop Agent API in an application is to to import and call the `getAgent` function from the FDC3 NPM module, which supports all standard's compliant Desktop Agents for web applications: + +```ts +import { DesktopAgent, getAgent } from "@finos/fdc3"; + +const fdc3: DesktopAgent = await getAgent(); +``` + +[For more details on the getAgent() function and arguments you can pass to it, see its reference page.](ref/GetAgent) + +::: + For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): @@ -28,24 +42,14 @@ Hence, from FDC3 2.2 onwards apps SHOULD call the `getAgent()` to retrieve a `De ::: +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the Identity Validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). + ### getAgent //TODO: rewrite this section after updating getAgent docs for using URLs and param for setting window.fdc3. // Provide a brief introduction and link to getAgent reference -:::tip - -To simplify migration of an app that works with `window.fdc3` to using `getAgent()`, simply set the `fdc3 property on the global object yourself, i.e.: - -```ts -getAgent({ appId: “yourApp@yourorg.org” })) -.then((fdc3: DesktopAgent) => { window.fdc3 = fdc3 }) -.catch((error) => { console.error(`Failed to retrieve FDC3 Desktop Agent: ${error}`) }); -``` - -::: - As Web applications can navigate (or be navigated by users) to different URLs and become different application, apps MUST pass details of their identity to `getAgent()`. This can be done in one of two ways. 1. Provide an appId field @@ -95,40 +99,7 @@ Applications MAY provide additional fields related to configuration or failover > Note: Applications SHOULD provide visual feedback to users indicating that the app is in the process of connecting. Once the FDC3 interface is accessible the application SHOULD update that visual feedback. -#### Failover function - -Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. - -Example: Decreasing the timeout and providing a failover function - -```js - const fdc3 = await getAgent({ - appId: “myApp@yourorg.org”, - timeout: 250, - failover: async (params) => { - // return WindowProxy | URL | DesktopAgent - } - }); -``` - -The failover function allows an application to provide a backup mechanism for connecting to a DA. It is called only when establishment through normal procedures fails or times out. - -> Note - Failover can occur quicker than the timeout. For instance when an end user opens an FDC3 app in a new browser tab it will immediately failover because there will be no injected DesktopAgent and there will be no parent or opener windows. - -> Note - A second timeout is started when the failover function is called. So the total possible elapsed time to establish a connection is 2X the established timeout when a failover function is provided. - -> 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. - -Failover functions MUST be asynchronous MUST resolve to one of the following types: - -1) DesktopAgent - The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. -2) WindowProxy (Window object) - The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. -3) URL - If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. - If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. diff --git a/schemas/api/README.md b/schemas/api/README.md index 65ba8c51f..95457be02 100644 --- a/schemas/api/README.md +++ b/schemas/api/README.md @@ -2,9 +2,9 @@ The _schemas/api_ folder contains JSONSchema definitions that are used to implement wire protocols for an app working with a Desktop Agent, and for import into the Bridging wire protocols that shares many of the same structures. -Please note: Quicktype, the chosen generation tool currently has some limitations that prevent fully automatic schema generation from the existing TS types. For example it can not handle interfaces that contain methods in their definition. It also fails to generate schemas even if a type contains unused references to other types or interfaces that contain async functions (Promise return types). Therefore, in order to generate the `api\schemas\api.schema.json` some manual intervention was needed. +Please note: Quicktype, the chosen generation tool currently has some limitations that prevent fully automatic schema generation from the existing TS types. For example it can not handle interfaces that contain methods in their definition (as you can't define methods in JSON). It also fails to generate schemas even if a type contains unused references to other types or interfaces that contain async functions (Promise return types). Therefore, in order to generate the `api\schemas\api.schema.json` some manual intervention was needed. -Once these limitations are not an issue the `api\schemas\t2sQuicktypeUtil.js` script should be moved to the root level of the project and a new npm script `"api-schema-gen": "node t2sQuicktypeUtil.js src/api schemas/api/api.schema.json"` should be added. +Once these limitations are not an issue the `api\schemas\t2sQuicktypeUtil.js` script should be moved to the root level of the project and a new npm script `"api-schema-gen": "node t2sQuicktypeUtil.js src/api schemas/api/api.schema.json"` should be added. Alternatively, schemas (for API types) may be manually maintained against the matching TypeScript definitions Contents: @@ -17,3 +17,13 @@ Contents: - `api\schemas\*Request.schema.json` - Schemas defining request messages sent from apps to Desktop Agents. - `api\schemas\*Response.schema.json` - Schemas defining responses from DAs to apps for request messages (sent from apps to Desktop Agents). - `api\schemas\*Event.schema.json` - Schemas defining event messages sent from Desktop Agents to Apps. + +Please note that when adding a particular message type, that it needs its own schema file, which will declare the type (string). That type string MUST also be added to an enumeration in the base message schema it was derived from - each base message schema (appRequest, agentResponse, agentEvent) has an enumeration of the valid types and it must appear in that or the message will not validate. Unhelpfully, if you've forgotten to do that Quicktype will only report: + +``` +Error: Internal error: We got an empty string type. +``` + +or another similar error - its not always the same one! + +It can be very hard to figure out in which file the problem occurs. Generally, to figure out where an issue is, you can enable Quicktype's debug output by adding `--debug all` or `--debug print-schema-resolving` to the arguments assembled in s2tQuicktypeUtil.js or by taking the command it constructs (printed to the console) and manually add the option and re-run the command. If you're lucky, the error will be hit during the resolution steps which will point to the file(s) with an issue (often a disagreement between types combined with `allOf`). diff --git a/website/sidebars.json b/website/sidebars.json index 9d58dbcde..254f68161 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -31,7 +31,7 @@ "api/specs/preloadDesktopAgents", "api/specs/browserDesktopAgents", "api/specs/webConnectionProtocol", - "api/specs/browserCommunicationProtocol" + "api/specs/desktopAgentCommunicationProtocol" ] } ] From d21a179e6f0cb564c6194a119ba9a8261e7ae35f Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 31 Jul 2024 11:10:18 +0100 Subject: [PATCH 049/152] Switch to more readable syntax highlighting theme --- website/docusaurus.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 4172ef722..d46629edc 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -50,7 +50,8 @@ module.exports={ "plugins": [], "themeConfig": { "prism": { - "additionalLanguages": ["json","csharp"] + "additionalLanguages": ["json","csharp","typescript"], + "theme": require('prism-react-renderer/themes/vsDark') }, "algolia": { "appId": "YW91L9TW76", From 0b87318ea8abea438270c1955bb9dfb54636f836 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 31 Jul 2024 12:04:57 +0100 Subject: [PATCH 050/152] Reworked GetAgent page and source file --- docs/api/ref/GetAgent.md | 128 +++++++++------- src/api/GetAgent.ts | 310 ++++++++++++++++++++++----------------- 2 files changed, 248 insertions(+), 190 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index f32ba614e..2415c7b88 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -4,48 +4,57 @@ sidebar_label: GetAgent title: GetAgent --- -The `getAgent()` is the recommended way to connect to a Desktop Agent in a web applications. The function allows web applications to retrieve a Desktop Agent API interface to work with, whether they are running in an environment that supports injection of the Desktop Agent API (e.g. a Desktop container or browser with an extension) or in a standard web browser, where a proxy based on the [FDC3 Web Connection Protocol (WCP)](../specs/webConnectionProtocol) and [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) is used to connect to Desktop Agent. Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries. +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -The small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. +The `getAgent()` function is the recommended way for **web applications** to connect to an FDC3 Desktop Agent. The function is exported by the [`@finos/fdc3`](https://www.npmjs.com/package/@finos/fdc3) NPM module and returns a [Desktop Agent](./DesktopAgent) implementation: -As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). + + + +```ts +import { getAgent, DesktopAgent } from "@finos/fdc3"; + +try { + const fdc3: DesktopAgent = await getAgent(); + //do FDC3 things here +} catch (e) { + // Failed to connect +} +``` + + + + +```js +import { getAgent } from "@finos/fdc3"; + +try { + const fdc3 = await getAgent(); + //do FDC3 things here +} catch (e) { + // Failed to connect +} +``` -If no Desktop Agent is found, or an issue prevent connection to it, the `getAgent()` function will eventually reject its promise, which apps can handle to set themselves up to run without connection to a Desktop Agent or to display an error. + + + +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. + +The definition of the `getAgent()` function is as follows: ```ts -/** - * Function used to retrieve an FDC3 Desktop Agent API instance, which - * supports the discovery of 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). Finally, 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. - * - * @param {GetAgentParams} params Optional parameters object, which - * may include a URL to use for the app's identity, other settings - * that affect the behavior of the getAgent() function and a `failover` - * function that should be run if a Desktop Agent is not detected. - * - * @returns A promise that resolves to a DesktopAgent implementation or - * rejects with an error message from the `AgentError` enumeration if unable to - * return a Desktop Agent implementation. - * - * @example - * const fdc3 = await getAgent(); - * // OR - * getAgent({ - * identityUrl: "https://example.com/path?param=appName#example", - * channelSelector: false, - * intentresolver: false - * }).then((fdc3) => { /* do FDC3 stuff */}; - */ type getAgent = ( - params?: GetAgentParams, -): Promise; + params?: GetAgentParams, +) => Promise; +``` +A small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. +```ts /** * @typedef {Object} GetAgentParams Type representing parameters passed to the * getAgent function. @@ -78,7 +87,7 @@ type getAgent = ( * @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 `false` will inhibit that behaviour, leaving `window.fdc3` unset. + * this flag to `false` will inhibit that behavior, leaving `window.fdc3` unset. * * @property {function} failover A optional function that provides an * means of connecting to or starting a Desktop Agent, which will be called @@ -90,14 +99,24 @@ type getAgent = ( * and Desktop Agent Communication Protocols. */ type GetAgentParams = { - timeout: number = 1000, + timeout: number, identityUrl?: string, - channelSelector: boolean = true, - intentResolver: boolean = true, - dontSetWindowFdc3: boolean = false, + channelSelector: boolean, + intentResolver: boolean, + dontSetWindowFdc3: boolean, failover?: (args: GetAgentParams) => Promise -}; +}; +``` + +:::note +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). + +::: + +Finally, if there is still no Desktop Agent available, or an issue prevent connection to it, the `getAgent()` function will reject its promise with a message from the `AgentError` enumeration. + +```ts /** * Contains constants representing the errors that can be encountered when * trying to connect to a web-based Desktop Agent with the getAgent function. @@ -125,7 +144,7 @@ enum AgentError { ## Failover function -Interface retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. +Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. Example: Decreasing the timeout and providing a failover function @@ -133,35 +152,32 @@ Example: Decreasing the timeout and providing a failover function const fdc3 = await getAgent({ appId: “myApp@yourorg.org”, timeout: 250, - failover: async (params) => { - // return WindowProxy | URL | DesktopAgent + failover: async (params: GetAgentParams) => { + // return WindowProxy | DesktopAgent } }); ``` The failover function allows an application to provide a backup mechanism for connecting to a DA. It is called only when establishment through normal procedures fails or times out. -> Note - Failover can occur quicker than the timeout. For instance when an end user opens an FDC3 app in a new browser tab it will immediately failover because there will be no injected DesktopAgent and there will be no parent or opener windows. +:::note -> Note - A second timeout is started when the failover function is called. So the total possible elapsed time to establish a connection is 2X the established timeout when a failover function is provided. +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. -> 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. +::: -Failover functions MUST be asynchronous MUST resolve to one of the following types: +Failover functions MUST be asynchronous MUST resolve to one of the following types: -1) DesktopAgent +1. [`DesktopAgent`](./DesktopAgent) The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. -2) WindowProxy (Window object) - The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. -3) URL - If a URL is provided, then `getAgent()` will load that url in a hidden iframe and attempt to establish connectivity to a browser-resident DA within that iframe. +2. [`WindowProxy`](https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-windowproxy-exotic-object) ([MDN](https://developer.mozilla.org/en-US/docs/Glossary/WindowProxy)) + The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Ensure that the iframe has loaded (listen for the `load` event) or for separate windows allow a suitable timeout for the window load, then resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. - If the failover function returns any other result, or if communication cannot be established with the provided `WindowProxy` or URL within the specified timeout, then `getAgent()` will reject with the "AgentNotFound" error. -## Persisted Connection Data -The `getAgent()` function uses SessionStorage ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage), [HTML Living Standard](https://html.spec.whatwg.org/multipage/webstorage.html)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. Applications are not expected to interact with this information directly, rather it is set and used by the `getAgent()` implementation. +## Persisted Connection Data +The `getAgent()` function uses [`SessionStorage`](https://html.spec.whatwg.org/multipage/webstorage.html) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. Applications are not expected to interact with this information directly, rather it is set and used by the `getAgent()` implementation. The details persisted conform to the following type: @@ -214,7 +230,7 @@ type DesktopAgentDetails = { * Desktop Agent. Each 'type' refers to the means by which * a connection to the agent is made and/or an interface to it * received. */ -enum WebDesktopAgentType = { +enum WebDesktopAgentType { /** Denotes Desktop Agents that inject the FDC3 interface * at `window.fdc3`. */ PRELOAD = "PRELOAD", diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 1ba2532af..a1dc18c74 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -3,142 +3,184 @@ * for display in markdown format. */ import { DesktopAgent } from './DesktopAgent'; -import { ImplementationMetadata } from './ImplementationMetadata'; -import { AppMetadata } from './AppMetadata'; -/** - * Retrieves an FDC3 DesktopAgent instance either from a Desktop Agent that supports - * injection or by using the FDC3 Web Connection Protocol (WCP) to establish a connection - * with a browser-resident Desktop Agent. - * - * @param {GetAgentParams} params Required parameters for the function. - * - * @return A promise that resolves to an object which contains a DesktopAgent, or which - * rejects with a string from the AgentError union if a DesktopAgent cannot be established. - * - * @example - * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp@myorg.com/appd” - * }); - * - * @example Using appDUrl - * const { desktopAgent: fdc3 } = await getAgent({ - * appId: “myApp”, - * appDUrl: "myorg.com/appd" - * }); - */ - -export type GetAgentFunction = ( - params: GetAgentParams -) => Promise<{ - desktopAgent: DesktopAgent; - implementationMetadata: ImplementationMetadata; - appMetadata: AppMetadata; -}>; - -/** - * @typedef {Object} GetAgentParams Type representing parameters passed to the - * getAgent function. - * - * @property {string} appId The fully qualified appId that represents the application. - * (in the form @) - * - * @property {URL} appDUrl A URL that points to an appD record for the application. - * Used as an alternative to providing a fully qualified appId. - * - * @property {number} timeout Number of milliseconds to allow for establishing a - * DesktopAgent. When the timeout expires, the optional provided failover function will be - * run. Default 750. - * - * @property {boolean} channelSelector Flag indicating that the application - * requires getAgent() to create a channel selector UI. Defaults to true. - * - * @property {boolean} intentResolver Flag indicating that the application - * requires getAgent() to create an intent resolver UI. Defaults to true. - * - * @property {function} failover A optional function that can establish connectivity - * to a DesktopAgent if standard mechanisms fail. If a WindowProxy or URL is provided - * then getAgent() will re-run its internal algorithm with those objects (restarting - * the timeout). The function may also simply return a DesktopAgent. - */ - -export type GetAgentParams = { - timeout?: number; // Defaults to 750 - appId?: string; - appDUrl?: string; - failover?: (args: GetAgentParams) => Promise; +/** + * Function used to retrieve an FDC3 Desktop Agent API instance, which + * supports the discovery of 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). Finally, 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. + * + * @param {GetAgentParams} params Optional parameters object, which + * may include a URL to use for the app's identity, other settings + * that affect the behavior of the getAgent() function and a `failover` + * function that should be run if a Desktop Agent is not detected. + * + * @returns A promise that resolves to a DesktopAgent implementation or + * rejects with an error message from the `AgentError` enumeration if unable to + * return a Desktop Agent implementation. + * + * @example + * const fdc3 = await getAgent(); + * + * // OR + * + * getAgent({ + * identityUrl: "https://example.com/path?param=appName#example", + * channelSelector: false, + * intentresolver: false + * }).then((fdc3) => { + * //do FDC3 stuff here + * }; + */ +export type getAgent = ( + params?: GetAgentParams, +) => Promise; + +/** + * @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} timeout Number of millisecs to allow for an fdc3 + * implementation to be found before calling the failover function or + * rejecting (default 1000). 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 {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). If not set will + * default to true. MAY be ignored by Desktop Agent Preload (container) + * implementations. + * + * @property {boolean} intentResolver Flag indicating that the application + * needs access to an intent resolver UI (i.e. because it supports raising on or + * more intents and and does not implement its own UI for selecting target apps. + * If not set will default 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 `false` will inhibit that behaviour, leaving `window.fdc3` unset. + * + * @property {function} failover A optional function that provides an + * 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, + identityUrl?: string, + channelSelector: boolean, + intentResolver: boolean, + dontSetWindowFdc3: boolean, + failover?: (args: GetAgentParams) => Promise +}; + +/** + * Contains constants representing the errors that can be encountered when + * trying to connect to a web-based Desktop Agent with the getAgent function. + */ +export enum AgentError { + /** Returned if no Desktop Agent was found by any means available or + * if the Agent previously connected to is not contactable on a + * subsequent connection attempt.*/ + AgentNotFound = "AgentNotFound", + + /** Returned if validation of the app identity by the Desktop Agent + * Failed or the app is not being allowed to connect to the Desktop Agent + * for another reason. */ + AccessDenied = "AccessDenied", + + /** Returned if an error or exception occurs while trying to set + * up communication with a Desktop Agent. */ + ErrorOnConnect = "ErrorOnConnect", + + /** Returned if either the failover function itself, or what it returned, + * was not the right type. */ + InvalidFailover = "InvalidFailover" }; -/** - * Represents the set of errors that may be returned by getAgent() if connectivity - * cannot be established with a DesktopAgent. - * - * "AgentNotFound" - Returned when connectivity to a DA cannot be established. - * - * "InvalidAgent" - Returned when a DA does not conform to the Web Connection Protocol (WCP). - * - * "IdentityValidationFailed" - Returned when the app fails DA identity verification. - * - * "NoAppIdentityProvided" - Returned when the DA refuses a connection from application. - * - * "AccessDenied" - TODO? Returned when the app does not pass one of the required identity parameters indicating an app directory record. - * - * "InvalidFailover" - Returned when the failover function itself, or its resolution is not the right type. - * - * "ReestablishConnectionFailed" - Returned when reestablishment of an instance via persisted StorageSession data fails (e.g. after a page navigation) - * - * "ErrorOnConnect" - Returned when any other error or exception occurs. - */ - -export type AgentError = - | 'AgentNotFound' - | 'InvalidAgent' - | 'IdentityValidationFailed' - | 'NoAppIdentityProvided' - | 'AccessDenied' - | 'ErrorOnConnect' - | 'InvalidFailover' - | 'ReestablishConnectionFailed'; - -/** - * Connection data from a previous call to getAgent() that may be persisted to SessionStorage. - * getAgent() will use this connection information when it exists to ensure a consistent instanceId. - */ -export type DesktopAgentDetails = { - /** The type of DA. Prevents an inadvertent switch to a different agent.*/ - - agentType: WebDesktopAgentType; - - /** May contain the URL that was used to connect to the prior DA. - * This may have been provided by a parent window that has since - * closed. It may therefore be used to open a new window/iframe and restart the DA. */ - - url?: string; - - /** The prior appId set by this window. If a appDUrl was previously set then - * this appId will be set to the the representative fully qualified appId. */ - - appId: string; - - /** The instanceId that was issued by the DA to this window. */ - - instanceId: string; - - /** The instanceUuid that was previously issued by the DA. - * This MUST be passed when connecting to the DA. The DA will use - * this to determine whether the app has previously connected and - * which instance it was. The DA uses this to reissue the same instanceId. - * - * The instanceUuid is secret. It should never be shared with other applications - * and is not available through the FDC3 API. */ - - instanceUuid: string; +/** Type representing data on the Desktop Agent that an app + * connected to that is persisted by the getAgent function + * to be used when re-connecting (after a navigation or + * refresh event) and to ensure a consistent instanceId. + */ +export type DesktopAgentDetails = { + /** The type of Desktop Agent connected to. Used to + * prevent an inadvertent switch to a different agent.*/ + agentType: WebDesktopAgentType, + + /** The URL that was previously sent to the Desktop Agent + * to establish the app's identity.*/ + identityUrl?: string, + + /** The current URL at the time of the last connection to + * a Desktop Agent.*/ + actualUrl?: string, + + /** Optional URL field that should be used to store any + * URL that was used to connect to a Desktop Agent. URLs + * may have been provided by a parent window that has since + * gone away and persisting may allow re-connection in such + * cases. */ + agentUrl?: string, + + /** The appId that was identified for the application by the + * Desktop Agent.*/ + appId: string, + + /** The instanceId that was issued to the app by the Desktop + * Agent. */ + instanceId: string, + + /** The instanceUuid that was issued to the app. This should be + * passed when connecting to the Desktop Agent to help + * identify that this app has connected before and which + * instance it is, enabling the Desktop Agent to reissue + * the same instanceId. The instanceUuid should never be shared + * with other applications and is not available through the + * FDC3 API, allowing it to be used as a shared secret with + * the Desktop Agent that issued the associated instanceId.*/ + instanceUuid: string }; -/** Specifies the means by which a connection to the DA is made. - * "INJECTED" - The DA injects the FDC3 interface at `window.fdc3`. - * "PARENT" - The DA runs in a parent window or iframe. - * "URL" - The DA is loaded as an iframe in the app window. - */ - -export type WebDesktopAgentType = 'INJECTED' | 'PARENT' | 'URL'; +/** Enumeration of values used to describe types of web-based +* Desktop Agent. Each 'type' refers to the means by which +* a connection to the agent is made and/or an interface to it +* received. */ +export enum WebDesktopAgentType { + /** Denotes Desktop Agents that inject the FDC3 interface + * at `window.fdc3`. */ + PRELOAD = "PRELOAD", + + /** 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`. */ + 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. */ + PROXY_URL = "PROXY_URL", + + /** Denotes a Desktop Agent that was returned by a failover + * function that was passed by the application. */ + FAILOVER = "FAILOVER" +}; From b0bca644a25f68ff8469e489110a111b26bd1c29 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 31 Jul 2024 12:20:46 +0100 Subject: [PATCH 051/152] rework supported-platforms --- docs/api/supported-platforms.md | 172 ++++++++++---------------------- 1 file changed, 55 insertions(+), 117 deletions(-) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 1f9faf20f..ec6bda670 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -16,173 +16,111 @@ The recommended way to get access to the FDC3 Desktop Agent API in an applicatio ```ts import { DesktopAgent, getAgent } from "@finos/fdc3"; -const fdc3: DesktopAgent = await getAgent(); -``` - -[For more details on the getAgent() function and arguments you can pass to it, see its reference page.](ref/GetAgent) - -::: - -For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). - -There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): - -- **Desktop Agent Preload**: Used where the Desktop Agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. -- **Desktop Agent Proxy**: Used when running in a standard web browser (without a browser extension or similar customization). The Desktop Agent will often be running in a different window or frame to the application and must be communicated with via cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of cross-document messaging, allowing the application to work with the FDC3 API directly. +//... -The FDC3 Standard defines a [Web Connection Protocol (WCP)](specs/webConnectionProtocol) that allows apps to work with either interface, by detecting which is applicable. The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. - -Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support any of the standardized interfaces. +try { + const fdc3: DesktopAgent = await getAgent(); + //do FDC3 things here +} catch (e: AgentError) { + //connection failed +} -:::note +//OR -In prior versions of FDC3 (<= 2.1) Apps were required to use the 'Desktop Agent Preload' interface, i.e. they relied on the existence of the `window.fdc3` object, which meant that apps running in a standard web browser had to import libraries specific to the Desktop Agent implementation in use. From FDC3 2.2 onwards the 'Desktop Agent Proxy' interface is available, which allows apps in a standard web browser to connect to any Desktop Agent that implements that interface. +getAgent().then((fdc3: DesktopAgent) => { + //do FDC3 things here +}).catch((e: AgentError) => { + //connection failed +}); +``` -Hence, from FDC3 2.2 onwards apps SHOULD call the `getAgent()` to retrieve a `DesktopAgent` API interface. +[For more details on the getAgent() function and arguments you can pass to it, see its reference page.](ref/GetAgent) ::: -As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the Identity Validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). - -### getAgent - -//TODO: rewrite this section after updating getAgent docs for using URLs and param for setting window.fdc3. -// Provide a brief introduction and link to getAgent reference +For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can by used by web applications to retrieve a `DesktopAgent` interface and to provide typing. Each FDC3-compliant Desktop Agent that the application runs in, can then provide an implementation of the FDC3 API operations. +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -As Web applications can navigate (or be navigated by users) to different URLs and become different application, apps MUST pass details of their identity to `getAgent()`. This can be done in one of two ways. - -1. Provide an appId field - - The appId SHOULD be _fully qualified_ (containing a domain name). The DA will then use this to construct a query to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/v2/apps/yourApp". - - Example: Obtaining an fdc3 interface - - ```js - import { getAgent } from "@finos/fdc3"; + + - try { - const fdc3 = await getAgent({ appId: “yourApp@yourorg.org” }); - } catch (e) { - // Failed to connect - } - ``` +```bash +npm install @finos/fdc3 +``` -2. Provide an appDUrl field + + - As an alternative to providing a fully qualified appId, apps MAY provide an `appDUrl` field that contains a link to an AppD definition for the app. +```bash +yarn add @finos/fdc3 +``` - Example: Obtaining an fdc3 interface using an AppD locator - ```JavaScript - try { - const fdc3 = await getAgent({ appDUrl: 'https://yourorg.org/appd/v2/apps/yourApp' }); - } catch (e) { - // console.log(e); // Failed to connect - } - ``` + + -3. Provide a both appId and appDUrl fields +```bash +pnpm install @finos/fdc3 +``` - The DA will construct the AppD query according to AppD endpoint rules. For instance, this will result in a query to "https://yourorg.org/appd/v2/apps/yourApp". + + - Example: Obtaining an fdc3 interface using an AppD locator - - ```js - try { - const fdc3 = await getAgent({ appId: "yourApp", appDUrl: 'https://yourorg.org/appd' }); - } catch (e) { - // console.log(e); // Failed to connect - } - ``` +There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): -Applications MAY provide additional fields related to configuration or failover support. See [GetAgentParams](ref/GetAgent) for those options. +- **Desktop Agent Preload**: Used where the Desktop Agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. +- **Desktop Agent Proxy**: Used when running in a standard web browser (without a browser extension or similar customization). The Desktop Agent will often be running in a different window or frame to the application and must be communicated with via cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of cross-document messaging, allowing the application to work with the FDC3 API directly. -> Note: Applications SHOULD provide visual feedback to users indicating that the app is in the process of connecting. Once the FDC3 interface is accessible the application SHOULD update that visual feedback. +The FDC3 Standard defines a [Web Connection Protocol (WCP)](specs/webConnectionProtocol) that allows apps to work with either interface, by detecting which is applicable, and [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) that standardizes the messaging protocol used for cross-document messaging over `postMessage` and `MessagePorts` in a web browser. +The FDC3 NPM module implements the `getAgent()` function defined by WCP and can return an injected Desktop Agent, a Desktop Agent Proxy, or other Desktop Agent implementation enabled by a non-standard interface. +Hence, FDC3 apps SHOULD obtain access to a `DesktopAgent` object (`fdc3`) by importing or loading the `@finos/fdc3` library and then calling the provided `getAgent()` function, ensuring that they can support any of the standardized interfaces. +:::info +In prior versions of FDC3 (<= 2.1) Apps were required to use the 'Desktop Agent Preload' interface, i.e. they relied on the existence of the `window.fdc3` object, which meant that apps running in a standard web browser had to import libraries specific to the Desktop Agent implementation in use. From FDC3 2.2 onwards the 'Desktop Agent Proxy' interface is available, which allows apps in a standard web browser to connect to any Desktop Agent that implements that interface. +Hence, from FDC3 2.2 onwards apps SHOULD call the `getAgent()` to retrieve a `DesktopAgent` API interface. +::: +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the Identity Validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). ### Usage -There are two main ways FDC3 can be used from web applications: +Once you've retrieved a `DesktopAgent` interface, there are two main ways FDC3 can be used from web applications: #### 1. Direct Usage -Simply rely on the global object being made available by your Desktop Agent, and address the API directly: +Simply use the interface you've retrieved and address the API directly: ```js -function sendData() { - window.fdc3.broadcast({ +async function sendData(fdc3: DesktopAgent) { + await fdc3.broadcast({ type: 'fdc3.instrument', id: { ticker: 'AAPL' } }) } -if (window.fdc3) { - sendData(); -} else { - window.addEventListener("fdc3Ready", sendData); -} -``` - -#### 2. NPM Wrapper - -FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can by used by web applications to target operations from the [API Specification](api/spec) in a consistent way. Each FDC3-compliant Desktop Agent that the application runs in, can then provide an implementation of the FDC3 API operations. - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - -```bash -npm install @finos/fdc3 -``` - - - - -```bash -yarn add @finos/fdc3 -``` - - - - -```bash -pnpm install @finos/fdc3 +const fdc3: DesktopAgent = await getAgent(); +await sendData(fdc3); ``` - - +#### 2. es6 Function Wrappers The npm package provides a wrapper around FDC3, allowing you to use it with ES6 import syntax: ```javascript -import * as fdc3 from '@finos/fdc3'; +import {raiseIntent} from '@finos/fdc3'; -await fdc3.raiseIntent('ViewAnalysis', { +await raiseIntent('ViewAnalysis', { type: 'fdc3.instrument', id: { ticker: 'AAPL' } }); ``` -It also includes a helper function you can use to wait for FDC3 to become available: - -```javascript -import { fdc3Ready, addIntentListener } from '@finos/fdc3' - -await fdc3Ready(); - -const listener = await addIntentListener('ViewAnalysis', instrument => { - // handle intent -}); -``` - ## Native The FDC3 Standard does not currently define wire formats for an app to communicate with a Desktop Agent, nor does it define language specific API bindings, other than JavaScript and TypeScript. Hence, for a native application to be FDC3-enabled, it needs to either: From 3af5b3c577b5fce3b7dbb4ff921b52a9a2bd3d8f Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 31 Jul 2024 12:35:22 +0100 Subject: [PATCH 052/152] Tweak to supported platforms content for native after merging changes --- docs/api/supported-platforms.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index ab3bb17bc..fa6d5c305 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -123,9 +123,16 @@ await raiseIntent('ViewAnalysis', { ## Native +The FDC3 Standard currently only defines language specific API bindings for JavaScript/TypeScript and .NET, but is intended to be implemented in other languages (which can make use of the [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) as a wire protocol, but need to define a suitable connection protocol, which includes a defined communication channel to do so). + +Hence, for a native application to be FDC3-enabled, it needs to either: + +- Make use of a shared library (such as a .NET DLL or JAR file) that provides it with an implementation of the FDC3 API (which ties it to a specific Desktop Agent implementation). +- Model itself as a Desktop Agent (rather than just an app working with one) and use the Agent Bridging protocol to connect to a Desktop Agent Bridge and work through it to interoperate with apps managed by other Desktop Agents. + ### .NET -For a .NET application to be FDC3-enabled, it needs to run in the context of a platform provider that makes the FDC3 API available. The manner in which you get a reference to the desktop agent can be highly dependent on the provider chosen. For those looking to implement your own desktop agent, a recommended and typical design is to register an instance of the desktop agent at startup which can be injected into any class constructors that need references through inversion of control. More details for creating your own DesktopAgent can be found in the [fdc3-dotnet repository](https://github.com/finos/fdc3-dotnet). +For a .NET application to be FDC3-enabled, it needs to run in the context of a platform provider that makes the FDC3 API available. The manner in which you get a reference to the desktop agent can be highly dependent on the provider chosen. For those looking to implement your own desktop agent, a recommended and typical design is to register an instance of the Desktop Agent at startup which can be injected into any class constructors that need references through inversion of control. More details for creating your own DesktopAgent can be found in the [fdc3-dotnet repository](https://github.com/finos/fdc3-dotnet). #### Usage From 69a4c08316077f4ffabf548b6e08ff8416143ecb Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 31 Jul 2024 12:43:01 +0100 Subject: [PATCH 053/152] Update errors page --- docs/api/ref/Errors.md | 133 +++++++++++------------------------------ 1 file changed, 34 insertions(+), 99 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 328eb09e8..0e0a7bfd0 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -7,6 +7,39 @@ import TabItem from '@theme/TabItem'; FDC3 API operations may sometimes result in an error, which must be returned to the caller. Errors should be returned by rejecting the promise returned by the API with a JavaScript `Error` object (or equivalent for the language of the implementation). The `Error` Object's message should be chosen from the appropriate Error enumeration below. +## `AgentError` + +Contains constants representing the errors that can be encountered when calling the [`getAgent`](getAgent) function to establish connectivity to a Desktop Agent. Primarily used with web applications, but may also be used in other language +implementations. + + + + +```ts +enum AgentError { + /** Returned if no Desktop Agent was found by any means available or + * if the Agent previously connected to is not contactable on a + * subsequent connection attempt.*/ + AgentNotFound = "AgentNotFound", + + /** Returned if validation of the app identity by the Desktop Agent + * Failed or the app is not being allowed to connect to the Desktop Agent + * for another reason. */ + AccessDenied = "AccessDenied", + + /** Returned if an error or exception occurs while trying to set + * up communication with a Desktop Agent. */ + ErrorOnConnect = "ErrorOnConnect", + + /** Returned if either the failover function itself, or what it returned, + * was not the right type. */ + InvalidFailover = "InvalidFailover" +} +``` + + + + ## `ChannelError` Contains constants representing the errors that can be encountered when calling channels using the [`joinUserChannel`](DesktopAgent#joinuserchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](Channel#getcurrentcontext), [`broadcast`](Channel#broadcast) or [`addContextListener`](Channel#addcontextlistener) methods on the `Channel` object. @@ -344,107 +377,9 @@ public static class ResultError - [`DesktopAgent.raiseIntent`](DesktopAgent#raiseintent) - [`IntentResolution`](Metadata#intentresolution) -## `AgentError` - -Contains constants representing the errors that can be encountered when calling the [`getAgent`](getAgent) function to establish connectivity to a Desktop Agent. Primarily used with web applications, but may also be used in other language -implementations. - - - - -```ts -enum AgentError { - /** Returned when connectivity to a DA cannot be established via any - * available strategy.*/ - AgentNotFound = "AgentNotFound", - - /** Returned when a browser-based Desktop agent does not conform to the FDC3 - * Web Connection Protocol.*/ - InvalidAgent = "InvalidAgent", - - /** Returned when the app fails DA identity verification.**/ - IdentityValidationFailed = "IdentityValidationFailed", - - /** Returned when the app does not pass one of the required identity - * parameters indicating an app directory record.*/ - NoAppIdentityProvided = "NoAppIdentityProvided", - - /** Returned when the DA refuses a connection from application.*/ - AccessDenied = "AccessDenied", - - /** Returned when the failover function itself, or its resolution is not the - * right type.*/ - InvalidFailover = "InvalidFailover", - - /** Returned when reestablishment of an instance via persisted StorageSession - * data fails (e.g. after a page navigation).*/ - ReestablishConnectionFailed = "ReestablishConnectionFailed" - - /** Returned when any other error or exception occurs.*/ - ErrorOnConnect ="ErrorOnConnect" -} - -``` - - - - -```csharp -public static class ResultError -{ - /// - /// Returned when connectivity to a DA cannot be established via any - /// available strategy. - /// - public static readonly string AgentNotFound = nameof(AgentNotFound); - - /// - /// Returned when a browser-based Desktop agent does not conform to the FDC3 - /// Web Connection Protocol. - /// - public static readonly string InvalidAgent = nameof(InvalidAgent); - - /// - /// Returned when the app fails DA identity verification. - /// - public static readonly string IdentityValidationFailed = nameof(IdentityValidationFailed); - - /// - /// Returned when the app does not pass one of the required identity - /// parameters indicating an app directory record. - /// - public static readonly string NoAppIdentityProvided = nameof(NoAppIdentityProvided); - - /// - /// Returned when the DA refuses a connection from application. - /// - public static readonly string AccessDenied = nameof(AccessDenied); - - /// - /// Returned when the failover function itself, or its resolution is not the - /// right type. - /// - public static readonly string InvalidFailover = nameof(InvalidFailover); - - /// - /// Returned when reestablishment of an instance via persisted StorageSession - /// data fails (e.g. after a page navigation). - /// - public static readonly string ReestablishConnectionFailed = nameof(ReestablishConnectionFailed); - - /// - /// Returned when any other error or exception occurs. - /// - public static readonly string ErrorOnConnect = nameof(ErrorOnConnect); -} -``` - - - - ## `BridgingError` -`@experimental` +[`@experimental`](../../fdc3-compliance#experimental-features) From d46ab5d7de578fbcd99f969684c745b0ee26981f Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 1 Aug 2024 15:41:14 +0100 Subject: [PATCH 054/152] minor tweaks to docusaurus config and supported platforms --- docs/api/supported-platforms.md | 2 +- website/docusaurus.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index fa6d5c305..a2b5c94cb 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -108,7 +108,7 @@ const fdc3: DesktopAgent = await getAgent(); await sendData(fdc3); ``` -#### 2. es6 Function Wrappers +#### 2. es6-style Function Wrappers The npm package provides a wrapper around FDC3, allowing you to use it with ES6 import syntax: diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index d46629edc..0f8820350 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -50,7 +50,7 @@ module.exports={ "plugins": [], "themeConfig": { "prism": { - "additionalLanguages": ["json","csharp","typescript"], + "additionalLanguages": ["typescript","javascript","json","csharp"], "theme": require('prism-react-renderer/themes/vsDark') }, "algolia": { From 70ea90f5f92e9025717f4b771afd2a7a3c10569f Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 1 Aug 2024 16:22:47 +0100 Subject: [PATCH 055/152] Adding missing messages and docs for Intent Results Adding docs to raiseIntent*Response payloads and adding an IntentResultRequest and IntentResultResponse message exchange for the delivery of results (which can be void, but should still be sent to ensure correct responses to `intentResolution.getResult()`) --- schemas/api/agentResponse.schema.json | 1 + schemas/api/appRequest.schema.json | 1 + schemas/api/intentResultRequest.schema.json | 78 +++++++ schemas/api/intentResultResponse.schema.json | 29 +++ .../raiseIntentForContextResponse.schema.json | 4 + schemas/api/raiseIntentResponse.schema.json | 16 +- src/api/BrowserTypes.ts | 190 ++++++++++++++++-- src/bridging/BridgingTypes.ts | 20 +- src/context/ContextTypes.ts | 60 +++--- 9 files changed, 340 insertions(+), 59 deletions(-) create mode 100644 schemas/api/intentResultRequest.schema.json create mode 100644 schemas/api/intentResultResponse.schema.json diff --git a/schemas/api/agentResponse.schema.json b/schemas/api/agentResponse.schema.json index 14101c318..9fbed58c3 100644 --- a/schemas/api/agentResponse.schema.json +++ b/schemas/api/agentResponse.schema.json @@ -25,6 +25,7 @@ "getOrCreateChannelResponse", "getUserChannelsResponse", "intentListenerUnsubscribeResponse", + "intentResultResponse", "joinUserChannelResponse", "leaveCurrentChannelResponse", "openResponse", diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index fd2f30646..c63d35536 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -25,6 +25,7 @@ "getOrCreateChannelRequest", "getUserChannelsRequest", "intentListenerUnsubscribeRequest", + "intentResultRequest", "joinUserChannelRequest", "leaveCurrentChannelRequest", "openRequest", diff --git a/schemas/api/intentResultRequest.schema.json b/schemas/api/intentResultRequest.schema.json new file mode 100644 index 000000000..4f0826eae --- /dev/null +++ b/schemas/api/intentResultRequest.schema.json @@ -0,0 +1,78 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/IntentResultRequest.schema.json", + "type": "object", + "title": "IntentResult Request", + "description": "A request to deliver a result for an intent (which may include a `void` result that just indicates that the handler has run, returning no result).", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/IntentResultRequestType" + }, + "payload": { + "$ref": "#/$defs/IntentResultRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "IntentResultRequestType": { + "title": "IntentResult Request Message Type", + "const": "intentResultRequest" + }, + "IntentResultRequestPayload": { + "title": "IntentResult Request Payload", + "type": "object", + "properties": { + "intentResult": { + "title": "IntentResult", + "anyOf": [ + { + "type": "object", + "title": "IntentResult Context", + "properties": { + "context": { + "$ref": "../context/context.schema.json" + } + }, + "required": [ + "context" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Channel", + "properties": { + "channel": { + "$ref": "api.schema.json#/definitions/Channel" + } + }, + "required": [ + "channel" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Void", + "properties": {}, + "additionalProperties": false + } + ] + } + }, + "required": [ + "intentResult" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/intentResultResponse.schema.json b/schemas/api/intentResultResponse.schema.json new file mode 100644 index 000000000..ef520ba37 --- /dev/null +++ b/schemas/api/intentResultResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/IntentResultResponse.schema.json", + "type": "object", + "title": "IntentResult Response", + "description": "A response to a request to deliver an intent result.", + "allOf": [ + { + "$ref": "agentResponse.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/IntentResultResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "IntentResultResponseType": { + "title": "IntentResult Response Message Type", + "const": "intentResultResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/raiseIntentForContextResponse.schema.json b/schemas/api/raiseIntentForContextResponse.schema.json index 8496449ee..ef8b950a7 100644 --- a/schemas/api/raiseIntentForContextResponse.schema.json +++ b/schemas/api/raiseIntentForContextResponse.schema.json @@ -15,6 +15,8 @@ "$ref": "#/$defs/RaiseIntentForContextResponseType" }, "payload": { + "title": "RaiseIntentForContext Response Payload", + "description": "There are 3 possible responses to a raiseIntentForContext request, each of which sets a single property in the payload: Success (`intentResolution`), Needs further resolution (`appIntents`) or Error (`error`).", "oneOf": [ { "$ref": "raiseIntentResponse.schema.json#/$defs/RaiseIntentSuccessResponsePayload" @@ -43,6 +45,8 @@ "type": "object", "properties": { "appIntents": { + "title": "AppIntents", + "description": "Used if a raiseIntentForContext request requires additional resolution (e.g. by showing an intent resolver) before it can be handled.", "type": "array", "items": { "$ref": "api.schema.json#/definitions/AppIntent" diff --git a/schemas/api/raiseIntentResponse.schema.json b/schemas/api/raiseIntentResponse.schema.json index 8c0a5ef15..26179f615 100644 --- a/schemas/api/raiseIntentResponse.schema.json +++ b/schemas/api/raiseIntentResponse.schema.json @@ -15,6 +15,8 @@ "$ref": "#/$defs/RaiseIntentResponseType" }, "payload": { + "title": "RaiseIntent Response Payload", + "description": "There are 3 possible responses to a raiseIntent request, each of which sets a single property in the payload: Success (`intentResolution`), Needs further resolution (`appIntent`) or Error (`error`).", "oneOf": [ { "$ref": "#/$defs/RaiseIntentSuccessResponsePayload" @@ -38,11 +40,12 @@ "const": "raiseIntentResponse" }, "RaiseIntentSuccessResponsePayload": { - "title": "RaiseIntent Response Payload", - "description": "Response to a raiseIntent request that was successfully resolved", + "title": "RaiseIntent Success Response Payload", "type": "object", "properties": { "intentResolution": { + "title": "Intent Resolution", + "description": "Used if the raiseIntent request was successfully resolved", "$ref": "api.schema.json#/definitions/IntentResolution" } }, @@ -53,10 +56,12 @@ }, "RaiseIntentNeedsResolutionResponsePayload": { "title": "RaiseIntent NeedsResolution Response Payload", - "description": "Response to a raiseIntent request that needs additional resolution (i.e. show an intent resolver UI)", + "description": "Response to a raiseIntent request that needs additional resolution (i.e. show an intent resolver UI).", "type": "object", "properties": { "appIntent": { + "title": "AppIntent", + "description": "Used if a raiseIntent request requires additional resolution (e.g. by showing an intent resolver) before it can be handled.", "$ref": "api.schema.json#/definitions/AppIntent" } }, @@ -67,11 +72,12 @@ }, "RaiseIntentErrorResponsePayload": { "title": "RaiseIntent Error Response Payload", - "description": "Response to a raiseIntent request that resulted in an error", + "description": "Used if a raiseIntent request resulted in an error", "type": "object", "properties": { "error": { - "title": "RaiseIntent Error Response Payload", + "title": "RaiseIntent Error", + "description": "Should be set if the raiseIntent request returned an error.", "oneOf": [ { "$ref": "api.schema.json#/definitions/ResolveError" diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index ced5a0e59..4f2f2101a 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -50,6 +50,8 @@ // const intentEvent = Convert.toIntentEvent(json); // const intentListenerUnsubscribeRequest = Convert.toIntentListenerUnsubscribeRequest(json); // const intentListenerUnsubscribeResponse = Convert.toIntentListenerUnsubscribeResponse(json); +// const intentResultRequest = Convert.toIntentResultRequest(json); +// const intentResultResponse = Convert.toIntentResultResponse(json); // const joinUserChannelRequest = Convert.toJoinUserChannelRequest(json); // const joinUserChannelResponse = Convert.toJoinUserChannelResponse(json); // const leaveCurrentChannelRequest = Convert.toLeaveCurrentChannelRequest(json); @@ -571,7 +573,7 @@ export interface AgentResponseMessageResponsePayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type ResponseMessageType = "addContextListenerResponse" | "addEventListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; +export type ResponseMessageType = "addContextListenerResponse" | "addEventListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "intentResultResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; /** * A request message from an FDC3-enabled app to a Desktop Agent. @@ -611,7 +613,7 @@ export interface AppRequestMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -1247,6 +1249,8 @@ export interface Image { * listeners and used to identify it in messages (e.g. when unsubscribing). * * Unique identifier for a for an attempt to connect to a Desktop Agent + * + * Should be set if the raiseIntent request returned an error. */ export type FindInstancesErrors = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; @@ -1326,6 +1330,9 @@ export interface FindIntentResponsePayload { /** * An interface that relates an intent to apps + * + * Used if a raiseIntent request requires additional resolution (e.g. by showing an intent + * resolver) before it can be handled. */ export interface AppIntent { /** @@ -2389,6 +2396,74 @@ export interface IntentListenerUnsubscribeResponse { type: "intentListenerUnsubscribeResponse"; } +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to deliver a result for an intent (which may include a `void` result that just + * indicates that the handler has run, returning no result). + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface IntentResultRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: IntentResultRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "intentResultRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface IntentResultRequestPayload { + intentResult: PurpleIntentResult; +} + +export interface PurpleIntentResult { + context?: Context; + channel?: Channel; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to a request to deliver an intent result. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface IntentResultResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "intentResultResponse"; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -3024,6 +3099,10 @@ export interface RaiseIntentForContextResponse { * A payload for a response to an API call that will contain any return values or an `error` * property containing a standardized error message indicating that the request was * unsuccessful. + * + * There are 3 possible responses to a raiseIntentForContext request, each of which sets a + * single property in the payload: Success (`intentResolution`), Needs further resolution + * (`appIntents`) or Error (`error`). */ payload: RaiseIntentForContextResponsePayload; /** @@ -3038,20 +3117,34 @@ export interface RaiseIntentForContextResponse { * property containing a standardized error message indicating that the request was * unsuccessful. * - * Response to a raiseIntent request that was successfully resolved + * There are 3 possible responses to a raiseIntentForContext request, each of which sets a + * single property in the payload: Success (`intentResolution`), Needs further resolution + * (`appIntents`) or Error (`error`). * * Response to a raiseIntentForContext request that needs additional resolution (i.e. show * an intent resolver UI) * - * Response to a raiseIntent request that resulted in an error + * Used if a raiseIntent request resulted in an error */ export interface RaiseIntentForContextResponsePayload { - error?: FindInstancesErrors; + /** + * Should be set if the raiseIntent request returned an error. + */ + error?: FindInstancesErrors; + /** + * Used if the raiseIntent request was successfully resolved + */ intentResolution?: IntentResolution; - appIntents?: AppIntent[]; + /** + * Used if a raiseIntentForContext request requires additional resolution (e.g. by showing + * an intent resolver) before it can be handled. + */ + appIntents?: AppIntent[]; } /** + * Used if the raiseIntent request was successfully resolved + * * IntentResolution provides a standard format for data returned upon resolving an intent. * * ```javascript @@ -3146,6 +3239,10 @@ export interface RaiseIntentResponse { * A payload for a response to an API call that will contain any return values or an `error` * property containing a standardized error message indicating that the request was * unsuccessful. + * + * There are 3 possible responses to a raiseIntent request, each of which sets a single + * property in the payload: Success (`intentResolution`), Needs further resolution + * (`appIntent`) or Error (`error`). */ payload: RaiseIntentResponsePayload; /** @@ -3160,17 +3257,29 @@ export interface RaiseIntentResponse { * property containing a standardized error message indicating that the request was * unsuccessful. * - * Response to a raiseIntent request that was successfully resolved + * There are 3 possible responses to a raiseIntent request, each of which sets a single + * property in the payload: Success (`intentResolution`), Needs further resolution + * (`appIntent`) or Error (`error`). * * Response to a raiseIntent request that needs additional resolution (i.e. show an intent - * resolver UI) + * resolver UI). * - * Response to a raiseIntent request that resulted in an error + * Used if a raiseIntent request resulted in an error */ export interface RaiseIntentResponsePayload { - error?: FindInstancesErrors; + /** + * Should be set if the raiseIntent request returned an error. + */ + error?: FindInstancesErrors; + /** + * Used if the raiseIntent request was successfully resolved + */ intentResolution?: IntentResolution; - appIntent?: AppIntent; + /** + * Used if a raiseIntent request requires additional resolution (e.g. by showing an intent + * resolver) before it can be handled. + */ + appIntent?: AppIntent; } /** @@ -3194,7 +3303,7 @@ export interface RaiseIntentResultResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: ResponsePayload; + payload: RaiseIntentResultResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -3207,12 +3316,12 @@ export interface RaiseIntentResultResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface ResponsePayload { +export interface RaiseIntentResultResponsePayload { error?: ResponsePayloadError; - intentResult?: IntentResult; + intentResult?: FluffyIntentResult; } -export interface IntentResult { +export interface FluffyIntentResult { context?: Context; channel?: Channel; } @@ -3887,6 +3996,22 @@ export class Convert { return JSON.stringify(uncast(value, r("IntentListenerUnsubscribeResponse")), null, 2); } + public static toIntentResultRequest(json: string): IntentResultRequest { + return cast(JSON.parse(json), r("IntentResultRequest")); + } + + public static intentResultRequestToJson(value: IntentResultRequest): string { + return JSON.stringify(uncast(value, r("IntentResultRequest")), null, 2); + } + + public static toIntentResultResponse(json: string): IntentResultResponse { + return cast(JSON.parse(json), r("IntentResultResponse")); + } + + public static intentResultResponseToJson(value: IntentResultResponse): string { + return JSON.stringify(uncast(value, r("IntentResultResponse")), null, 2); + } + public static toJoinUserChannelRequest(json: string): JoinUserChannelRequest { return cast(JSON.parse(json), r("JoinUserChannelRequest")); } @@ -4753,6 +4878,23 @@ const typeMap: any = { { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, { json: "type", js: "type", typ: r("IntentListenerUnsubscribeResponseType") }, ], false), + "IntentResultRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("IntentResultRequestPayload") }, + { json: "type", js: "type", typ: r("IntentResultRequestType") }, + ], false), + "IntentResultRequestPayload": o([ + { json: "intentResult", js: "intentResult", typ: r("PurpleIntentResult") }, + ], false), + "PurpleIntentResult": o([ + { json: "context", js: "context", typ: u(undefined, r("Context")) }, + { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, + ], false), + "IntentResultResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("IntentResultResponseType") }, + ], false), "JoinUserChannelRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("JoinUserChannelRequestPayload") }, @@ -4920,14 +5062,14 @@ const typeMap: any = { ], false), "RaiseIntentResultResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, - { json: "payload", js: "payload", typ: r("ResponsePayload") }, + { json: "payload", js: "payload", typ: r("RaiseIntentResultResponsePayload") }, { json: "type", js: "type", typ: r("RaiseIntentResultResponseType") }, ], false), - "ResponsePayload": o([ + "RaiseIntentResultResponsePayload": o([ { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, - { json: "intentResult", js: "intentResult", typ: u(undefined, r("IntentResult")) }, + { json: "intentResult", js: "intentResult", typ: u(undefined, r("FluffyIntentResult")) }, ], false), - "IntentResult": o([ + "FluffyIntentResult": o([ { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, ], false), @@ -5088,6 +5230,7 @@ const typeMap: any = { "getOrCreateChannelResponse", "getUserChannelsResponse", "intentListenerUnsubscribeResponse", + "intentResultResponse", "joinUserChannelResponse", "leaveCurrentChannelResponse", "openResponse", @@ -5115,6 +5258,7 @@ const typeMap: any = { "getOrCreateChannelRequest", "getUserChannelsRequest", "intentListenerUnsubscribeRequest", + "intentResultRequest", "joinUserChannelRequest", "leaveCurrentChannelRequest", "openRequest", @@ -5281,6 +5425,12 @@ const typeMap: any = { "IntentListenerUnsubscribeResponseType": [ "intentListenerUnsubscribeResponse", ], + "IntentResultRequestType": [ + "intentResultRequest", + ], + "IntentResultResponseType": [ + "intentResultResponse", + ], "JoinUserChannelRequestType": [ "joinUserChannelRequest", ], diff --git a/src/bridging/BridgingTypes.ts b/src/bridging/BridgingTypes.ts index 4ec7d567e..e90803849 100644 --- a/src/bridging/BridgingTypes.ts +++ b/src/bridging/BridgingTypes.ts @@ -1089,6 +1089,8 @@ export interface PayloadClass { * Unique identifier for a response to a specific message and must always be accompanied by * a RequestUuid. * + * Should be set if the raiseIntent request returned an error. + * * Constants representing the errors that can be encountered when calling the `findIntent`, * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the * DesktopAgent (`fdc3`). @@ -3574,6 +3576,9 @@ export interface RaiseIntentAgentErrorResponseMeta { * Error message payload containing an standardized error string. */ export interface RaiseIntentAgentErrorResponsePayload { + /** + * Should be set if the raiseIntent request returned an error. + */ error: FindInstancesErrors; } @@ -3730,15 +3735,18 @@ export interface RaiseIntentAgentResponseMeta { } /** - * Response to a raiseIntent request that was successfully resolved - * * The message payload typically contains return values for FDC3 API functions. */ export interface RaiseIntentAgentResponsePayload { + /** + * Should be set if the raiseIntent request was successfully resolved + */ intentResolution: IntentResolution; } /** + * Should be set if the raiseIntent request was successfully resolved + * * IntentResolution provides a standard format for data returned upon resolving an intent. * * ```javascript @@ -3816,6 +3824,9 @@ export interface RaiseIntentBridgeErrorResponseMeta { * raised the original request. */ export interface RaiseIntentBridgeErrorResponsePayload { + /** + * Should be set if the raiseIntent request returned an error. + */ error: FindInstancesErrors; } @@ -3898,11 +3909,12 @@ export interface RaiseIntentBridgeResponseMeta { } /** - * Response to a raiseIntent request that was successfully resolved - * * The message payload typically contains return values for FDC3 API functions. */ export interface RaiseIntentBridgeResponsePayload { + /** + * Should be set if the raiseIntent request was successfully resolved + */ intentResolution: IntentResolution; } diff --git a/src/context/ContextTypes.ts b/src/context/ContextTypes.ts index 8b8c0d45a..8fccd9007 100644 --- a/src/context/ContextTypes.ts +++ b/src/context/ContextTypes.ts @@ -258,35 +258,35 @@ export interface InstrumentElement { */ export interface PurpleInstrumentIdentifiers { /** - * + * https://www.bloomberg.com/ */ BBG?: string; /** - * + * https://www.cusip.com/ */ CUSIP?: string; /** - * + * https://www.factset.com/ */ FDS_ID?: string; /** - * + * https://www.openfigi.com/ */ FIGI?: string; /** - * + * https://www.isin.org/ */ ISIN?: string; /** - * + * https://permid.org/ */ PERMID?: string; /** - * + * https://www.refinitiv.com/ */ RIC?: string; /** - * + * https://www.lseg.com/sedol */ SEDOL?: string; /** @@ -303,15 +303,15 @@ export interface PurpleInstrumentIdentifiers { */ export interface OrganizationMarket { /** - * + * https://www.bloomberg.com/ */ BBG?: string; /** - * + * https://www.iso.org/iso-3166-country-codes.html */ COUNTRY_ISOALPHA2?: string; /** - * + * https://en.wikipedia.org/wiki/Market_Identifier_Code */ MIC?: string; /** @@ -798,15 +798,15 @@ export interface OrganizationObject { */ export interface Identifiers { /** - * + * https://www.bloomberg.com/ */ BBG?: string; /** - * + * https://www.cusip.com/ */ CUSIP?: string; /** - * + * https://www.factset.com/ * * FactSet Permanent Identifier representing the organization * @@ -814,25 +814,25 @@ export interface Identifiers { */ FDS_ID?: string; /** - * + * https://www.openfigi.com/ */ FIGI?: string; /** - * + * https://www.isin.org/ */ ISIN?: string; /** - * + * https://permid.org/ * * Refinitiv Permanent Identifiers, or PermID for the organization */ PERMID?: string; /** - * + * https://www.refinitiv.com/ */ RIC?: string; /** - * + * https://www.lseg.com/sedol */ SEDOL?: string; /** @@ -1181,35 +1181,35 @@ export interface Instrument { */ export interface FluffyInstrumentIdentifiers { /** - * + * https://www.bloomberg.com/ */ BBG?: string; /** - * + * https://www.cusip.com/ */ CUSIP?: string; /** - * + * https://www.factset.com/ */ FDS_ID?: string; /** - * + * https://www.openfigi.com/ */ FIGI?: string; /** - * + * https://www.isin.org/ */ ISIN?: string; /** - * + * https://permid.org/ */ PERMID?: string; /** - * + * https://www.refinitiv.com/ */ RIC?: string; /** - * + * https://www.lseg.com/sedol */ SEDOL?: string; /** @@ -1226,15 +1226,15 @@ export interface FluffyInstrumentIdentifiers { */ export interface PurpleMarket { /** - * + * https://www.bloomberg.com/ */ BBG?: string; /** - * + * https://www.iso.org/iso-3166-country-codes.html */ COUNTRY_ISOALPHA2?: string; /** - * + * https://en.wikipedia.org/wiki/Market_Identifier_Code */ MIC?: string; /** From a628009100d6d7e53c9283f0a58ca34e954a7e30 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 1 Aug 2024 22:08:14 +0100 Subject: [PATCH 056/152] Update WCP to use identityUrl rather than appId/appDUrl for identity --- schemas/api/WCP1Hello.schema.json | 10 +++++++-- .../api/WCP4ValidateAppIdentity.schema.json | 13 ++++-------- src/api/BrowserTypes.ts | 21 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index f610710b1..83ce743ce 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -24,6 +24,12 @@ "title": "WCP1Hello Payload", "type": "object", "properties": { + "identityUrl": { + "title": "Identity URL", + "description": "URL to use for the identity of the application. Desktop Agents MUST validate that the origin of the message matches the URL, but MAY implement custom comparison logic.", + "type": "string", + "format": "uri" + }, "fdc3Version": { "title": "FDC3 version", "description": "The version of FDC3 API that the app supports.", @@ -40,11 +46,11 @@ "type": "boolean" } }, - "required": ["fdc3Version"] + "required": ["identityUrl","fdc3Version"] }, "meta": true }, - "required": ["type", "payload", "meta"], + "required": [ "type", "payload", "meta"], "additionalProperties": false } } diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json index d9c94006d..6ab04e85a 100644 --- a/schemas/api/WCP4ValidateAppIdentity.schema.json +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -24,14 +24,9 @@ "title": "WCP4ValidateAppIdentity Payload", "type": "object", "properties": { - "appId": { - "title": "appId", - "description": "appId for the application attempting to connect. The appId must be fully qualified (appId@host.domain.appD) such that the URL for the appD record can be determined for identity validation purposes, or appDUrl must also be specified.", - "type": "string" - }, - "appDUrl": { - "title": "appDUrl", - "description": "URL for an App Directory record that provides identity details for the application attempting to connect", + "identityUrl": { + "title": "Identity URL", + "description": "URL to use for the identity of the application. Desktop Agents MUST validate that the origin of the message matches the URL, but MAY implement custom comparison logic.", "type": "string", "format": "uri" }, @@ -48,7 +43,7 @@ }, "additionalProperties": false, "required": [ - "appId" + "identityUrl" ] }, "meta": true diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 4f2f2101a..9806ad0a8 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -3371,6 +3371,11 @@ export interface WebConnectionProtocol1HelloPayload { * The version of FDC3 API that the app supports. */ fdc3Version: string; + /** + * URL to use for the identity of the application. Desktop Agents MUST validate that the + * origin of the message matches the URL, but MAY implement custom comparison logic. + */ + identityUrl: string; /** * A flag that may be used to indicate that an intent resolver is or is not required. Set to * false if no intents, or only targeted intents, are raised @@ -3488,16 +3493,10 @@ export interface WebConnectionProtocol4ValidateAppIdentity { */ export interface WebConnectionProtocol4ValidateAppIdentityPayload { /** - * URL for an App Directory record that provides identity details for the application - * attempting to connect - */ - appDUrl?: string; - /** - * appId for the application attempting to connect. The appId must be fully qualified - * (appId@host.domain.appD) such that the URL for the appD record can be determined for - * identity validation purposes, or appDUrl must also be specified. + * URL to use for the identity of the application. Desktop Agents MUST validate that the + * origin of the message matches the URL, but MAY implement custom comparison logic. */ - appId: string; + identityUrl: string; /** * If an application has previously connected to the desktop agent, it may specify its prior * instance id and associated instance UUID to request the same same instance Id be assigned. @@ -5085,6 +5084,7 @@ const typeMap: any = { "WebConnectionProtocol1HelloPayload": o([ { json: "channelSelector", js: "channelSelector", typ: u(undefined, true) }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, + { json: "identityUrl", js: "identityUrl", typ: "" }, { json: "resolver", js: "resolver", typ: u(undefined, true) }, ], "any"), "WebConnectionProtocol2LoadURL": o([ @@ -5111,8 +5111,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("WebConnectionProtocol4ValidateAppIdentityType") }, ], false), "WebConnectionProtocol4ValidateAppIdentityPayload": o([ - { json: "appDUrl", js: "appDUrl", typ: u(undefined, "") }, - { json: "appId", js: "appId", typ: "" }, + { json: "identityUrl", js: "identityUrl", typ: "" }, { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, { json: "instanceUuid", js: "instanceUuid", typ: u(undefined, "") }, ], false), From 71f0a37e690967c7bf76521f13e5840174020fbe Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 2 Aug 2024 13:36:19 +0100 Subject: [PATCH 057/152] Minor addition to API access details in API overview --- docs/api/spec.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/spec.md b/docs/api/spec.md index 470c2760a..302dfc5f2 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -51,6 +51,8 @@ The FDC3 API specification consists of interfaces. It is expected that each Des Other interfaces defined in the spec are not critical to define as concrete types. Rather, the Desktop Agent should expect to have objects of the interface shape passed into or out of their library. +The means to access an API interface is defined separately for each language in which FDC3 is implemented. These definitions are important as they affect whether applicaitons can be written in a vendor agnostic format so that they run under any Standards-conformant implementation. For details of how to access an API interface in particular languages please see [Supported Platforms](supported-platforms). + ### Implementation language FDC3 and the Desktop Agent API it defines are intended to be independent of particular programming languages and platforms and hence the original definitions, produced in TypeScript, may be translated into other languages. However, this also places limitations on the API definitions as they need to be widely implementable in other languages. From 204e736a374dfe018648873b92f30abf5f07315a Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 2 Aug 2024 14:27:00 +0100 Subject: [PATCH 058/152] Correct spelling of FindIntentsByContextResponse --- .../findIntentsByContextResponse.schema.json | 2 +- src/api/BrowserTypes.ts | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/schemas/api/findIntentsByContextResponse.schema.json b/schemas/api/findIntentsByContextResponse.schema.json index 2957b7bf7..9ccf5cce1 100644 --- a/schemas/api/findIntentsByContextResponse.schema.json +++ b/schemas/api/findIntentsByContextResponse.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json", "type": "object", - "title": "FindIntentsByContexts Response", + "title": "FindIntentsByContext Response", "description": "A response to a findIntentsByContext request.", "allOf": [ { diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 9806ad0a8..9ec2c887d 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextsResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -25,7 +25,7 @@ // const findIntentRequest = Convert.toFindIntentRequest(json); // const findIntentResponse = Convert.toFindIntentResponse(json); // const findIntentsByContextRequest = Convert.toFindIntentsByContextRequest(json); -// const findIntentsByContextsResponse = Convert.toFindIntentsByContextsResponse(json); +// const findIntentsByContextResponse = Convert.toFindIntentsByContextResponse(json); // const getAppMetadataRequest = Convert.toGetAppMetadataRequest(json); // const getAppMetadataResponse = Convert.toGetAppMetadataResponse(json); // const getCurrentChannelRequest = Convert.toGetCurrentChannelRequest(json); @@ -1407,7 +1407,7 @@ export interface FindIntentsByContextRequestPayload { * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface FindIntentsByContextsResponse { +export interface FindIntentsByContextResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1417,7 +1417,7 @@ export interface FindIntentsByContextsResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: FindIntentsByContextsResponsePayload; + payload: FindIntentsByContextResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -1430,7 +1430,7 @@ export interface FindIntentsByContextsResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface FindIntentsByContextsResponsePayload { +export interface FindIntentsByContextResponsePayload { error?: FindInstancesErrors; appIntents?: AppIntent[]; } @@ -3795,12 +3795,12 @@ export class Convert { return JSON.stringify(uncast(value, r("FindIntentsByContextRequest")), null, 2); } - public static toFindIntentsByContextsResponse(json: string): FindIntentsByContextsResponse { - return cast(JSON.parse(json), r("FindIntentsByContextsResponse")); + public static toFindIntentsByContextResponse(json: string): FindIntentsByContextResponse { + return cast(JSON.parse(json), r("FindIntentsByContextResponse")); } - public static findIntentsByContextsResponseToJson(value: FindIntentsByContextsResponse): string { - return JSON.stringify(uncast(value, r("FindIntentsByContextsResponse")), null, 2); + public static findIntentsByContextResponseToJson(value: FindIntentsByContextResponse): string { + return JSON.stringify(uncast(value, r("FindIntentsByContextResponse")), null, 2); } public static toGetAppMetadataRequest(json: string): GetAppMetadataRequest { @@ -4655,12 +4655,12 @@ const typeMap: any = { { json: "context", js: "context", typ: r("Context") }, { json: "resultType", js: "resultType", typ: u(undefined, "") }, ], false), - "FindIntentsByContextsResponse": o([ + "FindIntentsByContextResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, - { json: "payload", js: "payload", typ: r("FindIntentsByContextsResponsePayload") }, - { json: "type", js: "type", typ: r("FindIntentsByContextsResponseType") }, + { json: "payload", js: "payload", typ: r("FindIntentsByContextResponsePayload") }, + { json: "type", js: "type", typ: r("FindIntentsByContextResponseType") }, ], false), - "FindIntentsByContextsResponsePayload": o([ + "FindIntentsByContextResponsePayload": o([ { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, { json: "appIntents", js: "appIntents", typ: u(undefined, a(r("AppIntent"))) }, ], false), @@ -5326,7 +5326,7 @@ const typeMap: any = { "FindIntentsByContextRequestType": [ "findIntentsByContextRequest", ], - "FindIntentsByContextsResponseType": [ + "FindIntentsByContextResponseType": [ "findIntentsByContextResponse", ], "GetAppMetadataRequestType": [ From a42798c5d38ab6972298219dc8568b8335b8d0b5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 20 Aug 2024 17:03:29 +0100 Subject: [PATCH 059/152] Make channelId nullable in broadcastEvent (for use with fdc3.open) --- schemas/api/broadcastEvent.schema.json | 13 ++++++++++--- src/api/BrowserTypes.ts | 10 ++++++---- src/bridging/BridgingTypes.ts | 10 +++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/schemas/api/broadcastEvent.schema.json b/schemas/api/broadcastEvent.schema.json index 3a8028255..90fc28f1f 100644 --- a/schemas/api/broadcastEvent.schema.json +++ b/schemas/api/broadcastEvent.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/broadcastEvent.schema.json", "type": "object", "title": "broadcast Event", - "description": "An event message from the Desktop Agent to an app indicating that context has been broadcast on a channel it is listening to.", + "description": "An event message from the Desktop Agent to an app indicating that context has been broadcast on a channel it is listening to, or specifically to this app instance if it was launched via `fdc3.open` and context was passed.", "allOf": [ { "$ref": "agentEvent.schema.json" @@ -33,8 +33,15 @@ "properties": { "channelId": { "title": "channel Id", - "description": "The Id of the channel that the broadcast was sent on.", - "type": "string" + "description": "The Id of the channel that the broadcast was sent on. May be `null` if the context is being broadcast due to a call `fdc3.open` that passed context.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, "context": { "$ref": "../context/context.schema.json", diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 9ec2c887d..f257dd5b8 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -617,7 +617,8 @@ export type RequestMessageType = "addContextListenerRequest" | "addEventListener /** * An event message from the Desktop Agent to an app indicating that context has been - * broadcast on a channel it is listening to. + * broadcast on a channel it is listening to, or specifically to this app instance if it was + * launched via `fdc3.open` and context was passed. * * A message from a Desktop Agent to an FDC3-enabled app representing an event. */ @@ -642,9 +643,10 @@ export interface BroadcastEvent { */ export interface BroadcastEventPayload { /** - * The Id of the channel that the broadcast was sent on. + * The Id of the channel that the broadcast was sent on. May be `null` if the context is + * being broadcast due to a call `fdc3.open` that passed context. */ - channelId: string; + channelId: null | string; /** * The context object that was broadcast. */ @@ -4504,7 +4506,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("BroadcastEventType") }, ], false), "BroadcastEventPayload": o([ - { json: "channelId", js: "channelId", typ: "" }, + { json: "channelId", js: "channelId", typ: u(null, "") }, { json: "context", js: "context", typ: r("Context") }, { json: "originatingApp", js: "originatingApp", typ: u(undefined, r("AppIdentifier")) }, ], false), diff --git a/src/bridging/BridgingTypes.ts b/src/bridging/BridgingTypes.ts index e90803849..ec0a3b481 100644 --- a/src/bridging/BridgingTypes.ts +++ b/src/bridging/BridgingTypes.ts @@ -3571,7 +3571,7 @@ export interface RaiseIntentAgentErrorResponseMeta { } /** - * Response to a raiseIntent request that resulted in an error + * Used if a raiseIntent request resulted in an error * * Error message payload containing an standardized error string. */ @@ -3739,13 +3739,13 @@ export interface RaiseIntentAgentResponseMeta { */ export interface RaiseIntentAgentResponsePayload { /** - * Should be set if the raiseIntent request was successfully resolved + * Used if the raiseIntent request was successfully resolved */ intentResolution: IntentResolution; } /** - * Should be set if the raiseIntent request was successfully resolved + * Used if the raiseIntent request was successfully resolved * * IntentResolution provides a standard format for data returned upon resolving an intent. * @@ -3818,7 +3818,7 @@ export interface RaiseIntentBridgeErrorResponseMeta { } /** - * Response to a raiseIntent request that resulted in an error + * Used if a raiseIntent request resulted in an error * * The error message payload contains details of an error return to the app or agent that * raised the original request. @@ -3913,7 +3913,7 @@ export interface RaiseIntentBridgeResponseMeta { */ export interface RaiseIntentBridgeResponsePayload { /** - * Should be set if the raiseIntent request was successfully resolved + * Used if the raiseIntent request was successfully resolved */ intentResolution: IntentResolution; } From f9fddc159404ca130f5335b9acc46290af92b370 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 21 Aug 2024 13:33:01 +0100 Subject: [PATCH 060/152] Add eventListenerUnsubscribeRequest/Response --- schemas/api/agentResponse.schema.json | 1 + schemas/api/appRequest.schema.json | 1 + ...extListenerUnsubscribeResponse.schema.json | 2 +- ...ventListenerUnsubscribeRequest.schema.json | 44 +++++++ ...entListenerUnsubscribeResponse.schema.json | 29 +++++ ...entListenerUnsubscribeResponse.schema.json | 2 +- src/api/BrowserTypes.ts | 111 +++++++++++++++++- 7 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 schemas/api/eventListenerUnsubscribeRequest.schema.json create mode 100644 schemas/api/eventListenerUnsubscribeResponse.schema.json diff --git a/schemas/api/agentResponse.schema.json b/schemas/api/agentResponse.schema.json index 9fbed58c3..1a30d1c14 100644 --- a/schemas/api/agentResponse.schema.json +++ b/schemas/api/agentResponse.schema.json @@ -15,6 +15,7 @@ "broadcastResponse", "contextListenerUnsubscribeResponse", "createPrivateChannelResponse", + "eventListenerUnsubscribeResponse", "findInstancesResponse", "findIntentResponse", "findIntentsByContextResponse", diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index c63d35536..72ea343fd 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -15,6 +15,7 @@ "broadcastRequest", "contextListenerUnsubscribeRequest", "createPrivateChannelRequest", + "eventListenerUnsubscribeRequest", "findInstancesRequest", "findIntentRequest", "findIntentsByContextRequest", diff --git a/schemas/api/contextListenerUnsubscribeResponse.schema.json b/schemas/api/contextListenerUnsubscribeResponse.schema.json index 0d47c6043..c9bd341e1 100644 --- a/schemas/api/contextListenerUnsubscribeResponse.schema.json +++ b/schemas/api/contextListenerUnsubscribeResponse.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeResponse.schema.json", "type": "object", "title": "ContextListenerUnsubscribe Response", - "description": "A response to a request to a contextListenerUnsubscribe request.", + "description": "A response to a contextListenerUnsubscribe request.", "allOf": [ { "$ref": "agentResponse.schema.json" diff --git a/schemas/api/eventListenerUnsubscribeRequest.schema.json b/schemas/api/eventListenerUnsubscribeRequest.schema.json new file mode 100644 index 000000000..b3569b254 --- /dev/null +++ b/schemas/api/eventListenerUnsubscribeRequest.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeRequest.schema.json", + "type": "object", + "title": "EventListenerUnsubscribe Request", + "description": "A request to unsubscribe an event listener.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/EventListenerUnsubscribeRequestType" + }, + "payload": { + "$ref": "#/$defs/EventListenerUnsubscribeRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "EventListenerUnsubscribeRequestType": { + "title": "EventListenerUnsubscribe Request Message Type", + "const": "eventListenerUnsubscribeRequest" + }, + "EventListenerUnsubscribeRequestPayload": { + "title": "EventListenerUnsubscribe Request Payload", + "type": "object", + "properties": { + "listenerUUID": { + "$ref": "common.schema.json#/$defs/ListenerUuid" + } + }, + "required": [ + "listenerUUID" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/eventListenerUnsubscribeResponse.schema.json b/schemas/api/eventListenerUnsubscribeResponse.schema.json new file mode 100644 index 000000000..465bf9d2a --- /dev/null +++ b/schemas/api/eventListenerUnsubscribeResponse.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeResponse.schema.json", + "type": "object", + "title": "EventListenerUnsubscribe Response", + "description": "A response to an eventListenerUnsubscribe request.", + "allOf": [ + { + "$ref": "agentResponse.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/EventListenerUnsubscribeResponseType" + }, + "payload": true, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "EventListenerUnsubscribeResponseType": { + "title": "EventListenerUnsubscribe Response Message Type", + "const": "eventListenerUnsubscribeResponse" + } + } +} \ No newline at end of file diff --git a/schemas/api/intentListenerUnsubscribeResponse.schema.json b/schemas/api/intentListenerUnsubscribeResponse.schema.json index aeaec0587..582ace791 100644 --- a/schemas/api/intentListenerUnsubscribeResponse.schema.json +++ b/schemas/api/intentListenerUnsubscribeResponse.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json", "type": "object", "title": "IntentListenerUnsubscribe Response", - "description": "A response to a request to a intentListenerUnsubscribe request.", + "description": "A response to a intentListenerUnsubscribe request.", "allOf": [ { "$ref": "agentResponse.schema.json" diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index f257dd5b8..abb920145 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -20,6 +20,8 @@ // const contextListenerUnsubscribeResponse = Convert.toContextListenerUnsubscribeResponse(json); // const createPrivateChannelRequest = Convert.toCreatePrivateChannelRequest(json); // const createPrivateChannelResponse = Convert.toCreatePrivateChannelResponse(json); +// const eventListenerUnsubscribeRequest = Convert.toEventListenerUnsubscribeRequest(json); +// const eventListenerUnsubscribeResponse = Convert.toEventListenerUnsubscribeResponse(json); // const findInstancesRequest = Convert.toFindInstancesRequest(json); // const findInstancesResponse = Convert.toFindInstancesResponse(json); // const findIntentRequest = Convert.toFindIntentRequest(json); @@ -573,7 +575,7 @@ export interface AgentResponseMessageResponsePayload { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type ResponseMessageType = "addContextListenerResponse" | "addEventListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "intentResultResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; +export type ResponseMessageType = "addContextListenerResponse" | "addEventListenerResponse" | "addIntentListenerResponse" | "broadcastResponse" | "contextListenerUnsubscribeResponse" | "createPrivateChannelResponse" | "eventListenerUnsubscribeResponse" | "findInstancesResponse" | "findIntentResponse" | "findIntentsByContextResponse" | "getAppMetadataResponse" | "getCurrentChannelResponse" | "getCurrentContextResponse" | "getInfoResponse" | "getOrCreateChannelResponse" | "getUserChannelsResponse" | "intentListenerUnsubscribeResponse" | "intentResultResponse" | "joinUserChannelResponse" | "leaveCurrentChannelResponse" | "openResponse" | "privateChannelAddEventListenerResponse" | "privateChannelDisconnectResponse" | "privateChannelUnsubscribeEventListenerResponse" | "raiseIntentForContextResponse" | "raiseIntentResponse" | "raiseIntentResultResponse"; /** * A request message from an FDC3-enabled app to a Desktop Agent. @@ -613,7 +615,7 @@ export interface AppRequestMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "eventListenerUnsubscribeRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -877,7 +879,7 @@ export interface ContextListenerUnsubscribeRequestPayload { */ /** - * A response to a request to a contextListenerUnsubscribe request. + * A response to a contextListenerUnsubscribe request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. @@ -1037,6 +1039,68 @@ export interface DisplayMetadata { */ export type Type = "app" | "private" | "user"; +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request to unsubscribe an event listener. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface EventListenerUnsubscribeRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: EventListenerUnsubscribeRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "eventListenerUnsubscribeRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface EventListenerUnsubscribeRequestPayload { + listenerUUID: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A response to an eventListenerUnsubscribe request. + * + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. + */ +export interface EventListenerUnsubscribeResponse { + /** + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. + */ + payload: BroadcastResponseResponsePayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "eventListenerUnsubscribeResponse"; +} + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -2375,7 +2439,7 @@ export interface IntentListenerUnsubscribeRequestPayload { */ /** - * A response to a request to a intentListenerUnsubscribe request. + * A response to a intentListenerUnsubscribe request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. @@ -3757,6 +3821,22 @@ export class Convert { return JSON.stringify(uncast(value, r("CreatePrivateChannelResponse")), null, 2); } + public static toEventListenerUnsubscribeRequest(json: string): EventListenerUnsubscribeRequest { + return cast(JSON.parse(json), r("EventListenerUnsubscribeRequest")); + } + + public static eventListenerUnsubscribeRequestToJson(value: EventListenerUnsubscribeRequest): string { + return JSON.stringify(uncast(value, r("EventListenerUnsubscribeRequest")), null, 2); + } + + public static toEventListenerUnsubscribeResponse(json: string): EventListenerUnsubscribeResponse { + return cast(JSON.parse(json), r("EventListenerUnsubscribeResponse")); + } + + public static eventListenerUnsubscribeResponseToJson(value: EventListenerUnsubscribeResponse): string { + return JSON.stringify(uncast(value, r("EventListenerUnsubscribeResponse")), null, 2); + } + public static toFindInstancesRequest(json: string): FindInstancesRequest { return cast(JSON.parse(json), r("FindInstancesRequest")); } @@ -4579,6 +4659,19 @@ const typeMap: any = { { json: "glyph", js: "glyph", typ: u(undefined, "") }, { json: "name", js: "name", typ: u(undefined, "") }, ], false), + "EventListenerUnsubscribeRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("EventListenerUnsubscribeRequestPayload") }, + { json: "type", js: "type", typ: r("EventListenerUnsubscribeRequestType") }, + ], false), + "EventListenerUnsubscribeRequestPayload": o([ + { json: "listenerUUID", js: "listenerUUID", typ: "" }, + ], false), + "EventListenerUnsubscribeResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, + { json: "type", js: "type", typ: r("EventListenerUnsubscribeResponseType") }, + ], false), "FindInstancesRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("FindInstancesRequestPayload") }, @@ -5221,6 +5314,7 @@ const typeMap: any = { "broadcastResponse", "contextListenerUnsubscribeResponse", "createPrivateChannelResponse", + "eventListenerUnsubscribeResponse", "findInstancesResponse", "findIntentResponse", "findIntentsByContextResponse", @@ -5249,6 +5343,7 @@ const typeMap: any = { "broadcastRequest", "contextListenerUnsubscribeRequest", "createPrivateChannelRequest", + "eventListenerUnsubscribeRequest", "findInstancesRequest", "findIntentRequest", "findIntentsByContextRequest", @@ -5298,6 +5393,12 @@ const typeMap: any = { "CreatePrivateChannelResponseType": [ "createPrivateChannelResponse", ], + "EventListenerUnsubscribeRequestType": [ + "eventListenerUnsubscribeRequest", + ], + "EventListenerUnsubscribeResponseType": [ + "eventListenerUnsubscribeResponse", + ], "FindInstancesRequestType": [ "findInstancesRequest", ], From 23020b1cdbcb6294909c8a8cea9e7d657675219a Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 23 Aug 2024 17:48:05 +0100 Subject: [PATCH 061/152] Make all getAgent params optional --- docs/api/ref/GetAgent.md | 12 ++++++------ src/api/GetAgent.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 2415c7b88..9335eb800 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -66,7 +66,7 @@ A small number of arguments are accepted that can affect the behavior of `getAge * 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} timeout Number of millisecs to allow for an fdc3 + * @property {number} timeout Number of milliseconds to allow for an fdc3 * implementation to be found before calling the failover function or * rejecting (default 1000). Note that the timeout is cancelled as soon as a * Desktop Agent is detected. There may be additional set-up steps to perform @@ -89,7 +89,7 @@ A small number of arguments are accepted that can affect the behavior of `getAge * if one does not already exist, and will fire the fdc3Ready event. Setting * this flag to `false` will inhibit that behavior, leaving `window.fdc3` unset. * - * @property {function} failover A optional function that provides an + * @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 @@ -99,11 +99,11 @@ A small number of arguments are accepted that can affect the behavior of `getAge * and Desktop Agent Communication Protocols. */ type GetAgentParams = { - timeout: number, + timeout?: number, identityUrl?: string, - channelSelector: boolean, - intentResolver: boolean, - dontSetWindowFdc3: boolean, + channelSelector?: boolean, + intentResolver?: boolean, + dontSetWindowFdc3?: boolean, failover?: (args: GetAgentParams) => Promise }; ``` diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index a1dc18c74..26db975e5 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -51,7 +51,7 @@ export type getAgent = ( * 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} timeout Number of millisecs to allow for an fdc3 + * @property {number} timeout Number of milliseconds to allow for an fdc3 * implementation to be found before calling the failover function or * rejecting (default 1000). Note that the timeout is cancelled as soon as a * Desktop Agent is detected. There may be additional set-up steps to perform @@ -72,9 +72,9 @@ export type getAgent = ( * @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 `false` will inhibit that behaviour, leaving `window.fdc3` unset. + * this flag to `false` will inhibit that behavior, leaving `window.fdc3` unset. * - * @property {function} failover A optional function that provides an + * @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 @@ -84,11 +84,11 @@ export type getAgent = ( * and Desktop Agent Communication Protocols. */ type GetAgentParams = { - timeout: number, + timeout?: number, identityUrl?: string, - channelSelector: boolean, - intentResolver: boolean, - dontSetWindowFdc3: boolean, + channelSelector?: boolean, + intentResolver?: boolean, + dontSetWindowFdc3?: boolean, failover?: (args: GetAgentParams) => Promise }; From 56a41bb0d7a866309d3e0d851caa9a70e6ee27e5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 23 Aug 2024 17:58:47 +0100 Subject: [PATCH 062/152] Adding disconnection step WCP6Goodbye to WCP --- schemas/api/WCP6Goodbye.schema.json | 35 +++++++++++++++++ schemas/api/WCPConnectionStep.schema.json | 3 +- src/api/BrowserTypes.ts | 47 ++++++++++++++++++++++- 3 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 schemas/api/WCP6Goodbye.schema.json diff --git a/schemas/api/WCP6Goodbye.schema.json b/schemas/api/WCP6Goodbye.schema.json new file mode 100644 index 000000000..39f1a4d34 --- /dev/null +++ b/schemas/api/WCP6Goodbye.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json", + "title": "Web Connection Protocol 6 Goodbye", + "description": "Goodbye message to be sent to the Desktop Agent when disconnecting (e.g. when closing the window or navigating). Desktop Agents should close the MessagePort after receiving this message, but retain instance details in case the application reconnects (e.g. after a navigation event).", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/WCP6GoodbyeBase" + }, + { + "$ref": "WCPConnectionStep.schema.json" + } + ], + "$defs": { + "WCP6GoodbyeBase": { + "type": "object", + "properties": { + "type": { + "title": "WCP6Goodbye Message Type", + "const": "WCP6Goodbye" + }, + "payload": { + "title": "WCP6Goodbye Payload", + "type": "object", + "properties": { }, + "required": [] + }, + "meta": true + }, + "required": [ "type", "payload", "meta"], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/schemas/api/WCPConnectionStep.schema.json b/schemas/api/WCPConnectionStep.schema.json index 9bdc76b19..1be4e5a10 100644 --- a/schemas/api/WCPConnectionStep.schema.json +++ b/schemas/api/WCPConnectionStep.schema.json @@ -14,7 +14,8 @@ "WCP3Handshake", "WCP4ValidateAppIdentity", "WCP5ValidateAppIdentityFailedResponse", - "WCP5ValidateAppIdentityResponse" + "WCP5ValidateAppIdentityResponse", + "WCP6Goodbye" ], "description": "Identifies the type of the connection step message." }, diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index abb920145..ad2fc587b 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -80,6 +80,7 @@ // const webConnectionProtocol4ValidateAppIdentity = Convert.toWebConnectionProtocol4ValidateAppIdentity(json); // const webConnectionProtocol5ValidateAppIdentityFailedResponse = Convert.toWebConnectionProtocol5ValidateAppIdentityFailedResponse(json); // const webConnectionProtocol5ValidateAppIdentitySuccessResponse = Convert.toWebConnectionProtocol5ValidateAppIdentitySuccessResponse(json); +// const webConnectionProtocol6Goodbye = Convert.toWebConnectionProtocol6Goodbye(json); // const webConnectionProtocolMessage = Convert.toWebConnectionProtocolMessage(json); // // These functions will throw an error if the JSON doesn't @@ -3649,6 +3650,31 @@ export interface WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload instanceUuid: string; } +/** + * Identifies the type of the connection step message. + */ + +/** + * Goodbye message to be sent to the Desktop Agent when disconnecting (e.g. when closing the + * window or navigating). Desktop Agents should close the MessagePort after receiving this + * message, but retain instance details in case the application reconnects (e.g. after a + * navigation event). + * + * A message used during the connection flow for an application to a Desktop Agent in a + * browser window. Used for messages sent in either direction. + */ +export interface WebConnectionProtocol6Goodbye { + meta: ConnectionStepMetadata; + /** + * The message payload, containing data pertaining to this connection step. + */ + payload: { [key: string]: any }; + /** + * Identifies the type of the connection step message. + */ + type: "WCP6Goodbye"; +} + /** * Identifies the type of the connection step message. */ @@ -3672,7 +3698,7 @@ export interface WebConnectionProtocolMessage { /** * Identifies the type of the connection step message. */ -export type ConnectionStepMessageType = "WCP1Hello" | "WCP2LoadUrl" | "WCP3Handshake" | "WCP4ValidateAppIdentity" | "WCP5ValidateAppIdentityFailedResponse" | "WCP5ValidateAppIdentityResponse"; +export type ConnectionStepMessageType = "WCP1Hello" | "WCP2LoadUrl" | "WCP3Handshake" | "WCP4ValidateAppIdentity" | "WCP5ValidateAppIdentityFailedResponse" | "WCP5ValidateAppIdentityResponse" | "WCP6Goodbye"; // Converts JSON strings to/from your types // and asserts the results of JSON.parse at runtime @@ -4301,6 +4327,14 @@ export class Convert { return JSON.stringify(uncast(value, r("WebConnectionProtocol5ValidateAppIdentitySuccessResponse")), null, 2); } + public static toWebConnectionProtocol6Goodbye(json: string): WebConnectionProtocol6Goodbye { + return cast(JSON.parse(json), r("WebConnectionProtocol6Goodbye")); + } + + public static webConnectionProtocol6GoodbyeToJson(value: WebConnectionProtocol6Goodbye): string { + return JSON.stringify(uncast(value, r("WebConnectionProtocol6Goodbye")), null, 2); + } + public static toWebConnectionProtocolMessage(json: string): WebConnectionProtocolMessage { return cast(JSON.parse(json), r("WebConnectionProtocolMessage")); } @@ -5229,6 +5263,11 @@ const typeMap: any = { { json: "instanceId", js: "instanceId", typ: "" }, { json: "instanceUuid", js: "instanceUuid", typ: "" }, ], false), + "WebConnectionProtocol6Goodbye": o([ + { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "payload", js: "payload", typ: m("any") }, + { json: "type", js: "type", typ: r("WebConnectionProtocol6GoodbyeType") }, + ], false), "WebConnectionProtocolMessage": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, { json: "payload", js: "payload", typ: m("any") }, @@ -5628,6 +5667,9 @@ const typeMap: any = { "WebConnectionProtocol5ValidateAppIdentitySuccessResponseType": [ "WCP5ValidateAppIdentityResponse", ], + "WebConnectionProtocol6GoodbyeType": [ + "WCP6Goodbye", + ], "ConnectionStepMessageType": [ "WCP1Hello", "WCP2LoadUrl", @@ -5635,5 +5677,6 @@ const typeMap: any = { "WCP4ValidateAppIdentity", "WCP5ValidateAppIdentityFailedResponse", "WCP5ValidateAppIdentityResponse", + "WCP6Goodbye", ], }; From b71d6229f970a704ac3c77947a02cc85f8e4131d Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 27 Aug 2024 11:40:40 +0100 Subject: [PATCH 063/152] Correct jsdoc for dontSetWindowFdc3 --- docs/api/ref/GetAgent.md | 2 +- src/api/GetAgent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 9335eb800..243ee8576 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -87,7 +87,7 @@ A small number of arguments are accepted that can affect the behavior of `getAge * @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 `false` will inhibit that behavior, leaving `window.fdc3` unset. + * 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 diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 26db975e5..d1e88d711 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -72,7 +72,7 @@ export type getAgent = ( * @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 `false` will inhibit that behavior, leaving `window.fdc3` unset. + * 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 From 54dbdb125fba4777893a5764d2bd140f79ffb74a Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 27 Aug 2024 11:45:40 +0100 Subject: [PATCH 064/152] Adding a note to fdc3Ready that its not needed if using getAgent() --- src/api/Methods.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/Methods.ts b/src/api/Methods.ts index c29b2f862..1e4a09baa 100644 --- a/src/api/Methods.ts +++ b/src/api/Methods.ts @@ -35,7 +35,7 @@ function rejectIfNoGlobal(f: () => Promise) { } /** - * Utility function that returns a promise that will resolve immeadiately + * Utility function that returns a promise that will resolve immediately * if the desktop agent API is found at `window.fdc3`. If the API is found, * the promise will resolve when the `fdc3Ready` event is received or if it * is found at the end of the specified timeout. If the API is not found, it @@ -46,6 +46,9 @@ function rejectIfNoGlobal(f: () => Promise) { * const intentListener = await addIntentListener("ViewChart", intentHandlerFn); * ``` * + * There is no need to use this function if you are using `await getAgent()` to + * retrieve a Desktop Agent as it already waits for the Desktop Agent to be ready. + * * @param waitForMs The number of milliseconds to wait for the FDC3 API to be * ready. Defaults to 5 seconds. */ From f29753a35587211b2fd62293642c6af548fef49b Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 27 Aug 2024 13:19:20 +0100 Subject: [PATCH 065/152] Adding acronyms and bridge protocol names to glossary + differentiate from web protocols --- docs/agent-bridging/spec.md | 14 +++++++------- docs/api/spec.md | 2 +- docs/fdc3-glossary.md | 9 +++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/agent-bridging/spec.md b/docs/agent-bridging/spec.md index 78dc2f3f9..009afe593 100644 --- a/docs/agent-bridging/spec.md +++ b/docs/agent-bridging/spec.md @@ -43,9 +43,9 @@ In any Desktop Agent Bridging scenario, it is expected that each DA is being ope The Desktop Agent Bridging Part of the FDC3 Standard is composed of three components: -- **[Connection](#connection)**: A means for Desktop Agents to communicate with a bridge, and through that bridge, with each other. -- **[Connection Protocol](#connection-protocol)**: A protocol defining message exchanges necessary to connect to a Bridge and to perform initial state synchronization. -- **[Messaging Protocol](#messaging-protocol)**: A protocol defining message exchanges that allow FDC3 API interop to extend across multiple Desktop Agents. +- **[Bridge Connection](#connection)**: A means for Desktop Agents to communicate with a bridge, and through that bridge, with each other. +- **[Bridge Connection Protocol (BCP)](#bridge-connection-protocol)**: A protocol defining message exchanges necessary to connect to a Bridge and to perform initial state synchronization. +- **[Bridge Messaging Protocol (BMP)](#bridge-messaging-protocol)**: A protocol defining message exchanges that allow FDC3 API interop to extend across multiple Desktop Agents. Detail on each of these components is defined in the following sections. @@ -69,7 +69,7 @@ Agent Bridging is introduced in FDC3 2.1 as an [@experimental](../fdc3-complianc ### JSON Message Protocol & JSON Schema -The connection and messaging protocols that the Desktop Agent Bridging Part defines are based on messages encoded in JSON. [JSON Schema](https://json-schema.org/) is used to define the format of each message in the protocol and should be considered the 'source of truth' for each and may be used to validate that individual messages are in the correct format. However, examples are provided in the documentation in TypeScript and JavaScript formats for convenience. TypeScript interfaces for individual messages, included in the FDC3 NPM module, are generated from the JSON Schema source files using [quicktype](https://quicktype.io/). +The Bridge Connection Protocol (BCP) and Bridge Messaging Protocols (BMP) that the Desktop Agent Bridging Part defines are based on messages encoded in JSON. [JSON Schema](https://json-schema.org/) is used to define the format of each message in the protocol and should be considered the 'source of truth' for each and may be used to validate that individual messages are in the correct format. However, examples are provided in the documentation in TypeScript and JavaScript formats for convenience. TypeScript interfaces for individual messages, included in the FDC3 NPM module, are generated from the JSON Schema source files using [quicktype](https://quicktype.io/). ## Connection @@ -156,7 +156,7 @@ Both DAs and bridge implementations SHOULD support at least connection to the re As the bridge binds its websocket on the loopback address (127.0.0.1) it cannot be connected to from another device. Hence, an instance of the standalone bridge may be run on each device and those instances exchange messages by other means in order to implement the bridge cross-device. -## Connection Protocol +## Bridge Connection Protocol On connection to the bridge's websocket, a handshake must be completed that may include an authentication step before a name is assigned to the Desktop Agent for use in routing messages. The purpose of the handshake is to allow: @@ -435,7 +435,7 @@ Desktop Agents SHOULD provide visual feedback to end users when they or other ag Although not part of the connection protocol, it should be noted that the `connectedAgentsUpdate` message sent in step 6 should also be sent whenever an agent disconnects from the bridge to update other agents. If any agents remain connected, then the `channelState` does not change and can be omitted. However, if the last agent disconnects the bridge SHOULD discard its internal `channelState`, instead of issuing the update. -## Messaging Protocol +## Bridge Messaging Protocol In order for Desktop Agents to communicate with the Desktop Agent Bridge and thereby other Desktop Agents, a messaging protocol is required. FDC3 supports both 'fire and forget' interactions (such as the broadcast of context messages) and interactions with specific responses (such as raising intents and returning a resolution and optional result), both of which must be handled by that messaging protocol and message formats it defines, as described in this section. @@ -1006,7 +1006,7 @@ However, `PrivateChannel` instances allow the registration of additional event h #### Message Schemas and generated sources -JSONSchema definitions are provided for all Desktop Agent Bridging message exchanges (see links in each reference page), which may be used to validate the correct generation of messages to or from a bridge (a separate schema is provided for the agent and bridge versions of each message). +JSONSchema definitions are provided for all Desktop Agent Bridging message exchanges defined by the Bridge Messaging Protocol (see links in each reference page), which may be used to validate the correct generation of messages to or from a bridge (a separate schema is provided for the agent and bridge versions of each message). The JSONSchema definitions are also used to generate TypeScript interfaces for the messages to aid in implementation of a Desktop Agent Bridge or client library. These may be imported from the FDC3 npm module: diff --git a/docs/api/spec.md b/docs/api/spec.md index 302dfc5f2..38eec38f5 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -80,7 +80,7 @@ Are all areas of functionality that any feature-complete desktop agent would imp A goal of the FDC3 Standard is that applications running in different Desktop Agent contexts on the same desktop, or operated by the same user, would be able to interoperate and that one Desktop Agent context would be able to discover and launch an application in another Desktop Application context. As Desktop Agent interop is supported by common standards for APIs an app in one Desktop Agent context would not need to know a different syntax to launch or interact with an app in another Desktop Agent context. -Inter-agent communication at the API layer may be achieved via the [Desktop Agent Bridging Part of the FDC3 Standard](../agent-bridging/spec) ([@experimental](../fdc3-compliance#experimental-features)), which defines an independent service that Desktop Agents may connect to, and a protocol for the exchange of messages relating to FDC3 API calls. Hence, by implementing support for Desktop Agent Bridging, a platform may extend interop across applications running in multiple Desktop Agent contexts. +Inter-agent communication at the API layer may be achieved via the [Desktop Agent Bridging Part of the FDC3 Standard](../agent-bridging/spec) ([@experimental](../fdc3-compliance#experimental-features)), which defines an independent service that Desktop Agents may connect to (via the Bridge Connection Protocol or BCP), and a protocol for the exchange of messages relating to FDC3 API calls (the Bridge Messaging Protocol or BMP). Hence, by implementing support for Desktop Agent Bridging, a platform may extend interop across applications running in multiple Desktop Agent contexts. Desktop Agent Bridging provides message exchanges and a workflow for performing intent resolution across multiple agents. Hence, app discovery is supported across the agents connected to the bridge for intent-based workflows. Further, as channels are also supported by bridging, context sharing also works across multiple agents. diff --git a/docs/fdc3-glossary.md b/docs/fdc3-glossary.md index b7a328d3b..ac161dca7 100644 --- a/docs/fdc3-glossary.md +++ b/docs/fdc3-glossary.md @@ -20,14 +20,15 @@ For the purposes of this Standard, the following definitions apply. Other terms - **application-specific intent**: A custom intent defined by an application or applications, independent of the Standard. - **Bridge**: Shorthand for Desktop Agent Bridge. - **bridging**: Shorthand for the exchange of messages across a Desktop Agent Bridge for the purposes of extending interop between apps managed by different Desktop Agents. +- **Bridge Connection Protocol (BCP)**: A defined set of steps for a Desktop Agent to connect to a Desktop Agent Bridge. +- **Bridge Messaging Protocol (BMP)**: Protocol for Desktop Agents to communicate with each other over a Desktop Agent Bridge. - **Channel**: A grouping of apps for the purposes of sharing stateful pieces of data. A secondary interface of the FDC3 API. - **context channels**: A mechanism to allow sets of apps to share stateful pieces of data among themselves, and to be alerted when that data changes. - **context**: Shorthand for context data. - **context data**: Objects encoding common identifiers and data in a standardized format that can be passed between apps via context channels or used in conjunction with intents to invoke actions creating a seamless cross-application workflow. Diverse context data types are created to encode different types of data, each having their own _type_ field and unique set of data fields. -- **DAB**: Acronym of Desktop Agent Bridge. - **Desktop Agent**: A desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. The primary interface of the FDC3 API. -- **Desktop Agent Bridge**: An independent service that Desktop Agents connect to which allows them to relay requests to other Desktop Agents also connected to the bridge, allowing FDC3-based interop to extend across multiple Desktop Agents and machines. -- **Desktop Agent Communication Protocol**: JSON communication protocol for the Desktop Agent API. Used by a Desktop Agent Proxy interface over the Channel Messaging API (as specified by the Web Connection Protocol) to communicate with a Desktop Agent running in another window/frame. +- **Desktop Agent Bridge (DAB)**: An independent service that Desktop Agents connect to which allows them to relay requests to other Desktop Agents also connected to the bridge, allowing FDC3-based interop to extend across multiple Desktop Agents and machines. +- **Desktop Agent Communication Protocol (DACP)**: JSON communication protocol for the Desktop Agent API. Used by a Desktop Agent Proxy interface over the Channel Messaging API (as specified by the Web Connection Protocol) to communicate with a Desktop Agent running in another window/frame. - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3` and may be returned by the `getAgent()` function defined in the FDC3 Web Connection Protocol. - **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Web Connection Protocol and Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. Returned by the `getAgent()` function provided by the FDC3 NPM module where a browser-based Desktop Agent is detected. - **FDC3 API**: A baseline, consistent developer interface for interoperability between applications. @@ -43,4 +44,4 @@ For the purposes of this Standard, the following definitions apply. Other terms - **resolving an intent**: The act of mapping a specified intent and context object to an application. - **standard intent**: An intent defined by this Standard. - **UUID**: Universally Unique IDentifier, synonymous with GUID. Defined by [IETF RFC4122](references). -- **Web Connection Protocol**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard, including both preloaded Desktop Agent interfaces and Browser-based Desktop Agents that work with a Desktop Agent Proxy. +- **Web Connection Protocol (WCP)**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard, including both preloaded Desktop Agent interfaces and Browser-based Desktop Agents that work with a Desktop Agent Proxy. From 6bb5699460019eea433877ee359a1949c360e940 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 29 Aug 2024 11:44:05 +0100 Subject: [PATCH 066/152] Adjusting iframe messages - Flipping Hello and Handshake messages, the UI now sends hello and the DA proxy setup by getAgent() responds with handshake - Added initial CSS to set on iframe to Hello message from iframe - Converted resize message into a restyle message that provides updated CSS - Adjusted drag message and made it generic (you might want to drag an intent resolver in addition to a channel selector) - removed location from iFrameChannels as it now duplicates the info in hello --- schemas/api/iFrameChannelResize.schema.json | 70 --- schemas/api/iFrameChannels.schema.json | 15 - ...rag.schema.json => iFrameDrag.schema.json} | 28 +- schemas/api/iFrameHandshake.schema.json | 10 +- schemas/api/iFrameHello.schema.json | 27 +- schemas/api/iFrameMessage.schema.json | 6 +- schemas/api/iFrameRestyle.schema.json | 72 +++ src/api/BrowserTypes.ts | 443 +++++++++++------- 8 files changed, 381 insertions(+), 290 deletions(-) delete mode 100644 schemas/api/iFrameChannelResize.schema.json rename schemas/api/{iFrameChannelDrag.schema.json => iFrameDrag.schema.json} (51%) create mode 100644 schemas/api/iFrameRestyle.schema.json diff --git a/schemas/api/iFrameChannelResize.schema.json b/schemas/api/iFrameChannelResize.schema.json deleted file mode 100644 index bd221eebb..000000000 --- a/schemas/api/iFrameChannelResize.schema.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelResize.schema.json", - "title": "iframe Channel Resize", - "description": "Message from the channel selector UI to the DA proxy when the user hits a toggle button that opens or closes the selector or otherwise resizes it. Includes the size that it should change to and the corner (if any) at which the change should be made.", - "type": "object", - "allOf": [ - { - "$ref": "#/$defs/IframeChannelResizeBase" - }, - { - "$ref": "iFrameMessage.schema.json" - } - ], - "$defs": { - "IframeChannelResizeBase": { - "type": "object", - "properties": { - "type": { - "title": "iframeChannelResize Message Type", - "const": "iframeChannelResize" - }, - "payload": { - "title": "iframeChannelResize Payload", - "type": "object", - "properties": { - "dimensions": { - "title": "Dimensions", - "description": "The updated dimensions of the UI", - "type": "object", - "properties": { - "width": { - "type": "integer" - }, - "height": { - "type": "integer" - } - }, - "required": ["width", "height"], - "additionalProperties": false - }, - "resizeAnchor": { - "title": "Resizing", - "description": "When resizing anchor at the indicated location,\ne.g.\n\t- for top-left and a larger size: the bottom right corner should move down and out.\n\t- for top and smaller size: both the bottom corners should move in and up.", - "anyOf": [ - { "type": "string", "const": "top-left" }, - { "type": "string", "const": "top" }, - { "type": "string", "const": "top-right" }, - { "type": "string", "const": "right" }, - { "type": "string", "const": "bottom-right" }, - { "type": "string", "const": "bottom" }, - { "type": "string", "const": "bottom-left" }, - { "type": "string", "const": "left" }, - { "type": "string", "const": "center" } - ] - } - }, - "additionalProperties": false, - "required": ["dimensions", "resizeAnchor"] - } - }, - "required": [ - "type", - "payload" - ], - "additionalProperties": false - } - } -} - diff --git a/schemas/api/iFrameChannels.schema.json b/schemas/api/iFrameChannels.schema.json index 9f5c69acc..c92ff72b8 100644 --- a/schemas/api/iFrameChannels.schema.json +++ b/schemas/api/iFrameChannels.schema.json @@ -38,21 +38,6 @@ "oneOf": [ {"type": "string"}, {"type": "null"} ] - }, - "location": { - "title": "Location", - "description": "If the channel selector was previously displayed in this window its location may be restored by setting the location coordinates", - "type": "object", - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "required": ["x", "y"], - "additionalProperties": false } }, "additionalProperties": false, diff --git a/schemas/api/iFrameChannelDrag.schema.json b/schemas/api/iFrameDrag.schema.json similarity index 51% rename from schemas/api/iFrameChannelDrag.schema.json rename to schemas/api/iFrameDrag.schema.json index eb52b6978..4f16e3573 100644 --- a/schemas/api/iFrameChannelDrag.schema.json +++ b/schemas/api/iFrameDrag.schema.json @@ -1,47 +1,47 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelDrag.schema.json", - "title": "iframe Channel Drag", - "description": "Message from the channel selector UI to the DA proxy when the user drags the selector to a new location.", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeDrag.schema.json", + "title": "iframe Drag", + "description": "Message from a UI iframe to the DA proxy (setup by `getAgent()`) indicating that the user is dragging the UI to a new location and providing the offset to apply to the location. The DA proxy implementation should limit the location to the current bounds of the window's viewport", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeChannelDragBase" + "$ref": "#/$defs/IframeDragBase" }, { "$ref": "iFrameMessage.schema.json" } ], "$defs": { - "IframeChannelDragBase": { + "IframeDragBase": { "type": "object", "properties": { "type": { - "title": "iframeChannelDrag Message Type", - "const": "iframeChannelDrag" + "title": "iframeDrag Message Type", + "const": "iframeDrag" }, "payload": { - "title": "iframeChannelDrag Payload", + "title": "iframeDrag Payload", "type": "object", "properties": { - "mouse": { - "title": "Dimensions", + "mouseOffsets": { + "title": "Mouse Offsets", "description": "The offset to move the frame by", "type": "object", "properties": { - "offsetX": { + "x": { "type": "integer" }, - "offsetY": { + "y": { "type": "integer" } }, - "required": ["offsetX", "offsetY"], + "required": ["x", "y"], "additionalProperties": false } }, "additionalProperties": false, - "required": ["mouse"] + "required": ["mouseOffsets"] } }, "required": [ diff --git a/schemas/api/iFrameHandshake.schema.json b/schemas/api/iFrameHandshake.schema.json index d79313b35..4e0b534a3 100644 --- a/schemas/api/iFrameHandshake.schema.json +++ b/schemas/api/iFrameHandshake.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", "title": "iframe Handshake", - "description": "Handshake message sent back by an iframe to the DA proxy code indicating that it is setup and ready to communicate over the MessagePort.", + "description": "Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) with a MessagePort appended over further communication is conducted.", "type": "object", "allOf": [ { @@ -24,14 +24,14 @@ "title": "iframeHandshake Payload", "type": "object", "properties": { - "implementationDetails": { - "title": "Implementation Details", + "fdc3Version": { + "title": "FDC3 version", "type": "string", - "description": "Details about the UI implementation in the iframe, such as vendor and version, for logging purposes." + "description": "The version of FDC3 API that the Desktop Agent will provide support for." } }, "additionalProperties": false, - "required": ["implementationDetails"] + "required": ["fdc3Version"] } }, "required": [ diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index a7cdb0332..b584c7c6f 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeHello.schema.json", "title": "iframe Hello", - "description": "Hello message sent by the DA proxy code in getAgent() to an iframe, that it has injected into the page, with a MessagePort appended that should be used for subsequent communication steps.", + "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate and containing initial CSS to set on the iframe.", "type": "object", "allOf": [ { @@ -24,14 +24,31 @@ "title": "iframeHello Payload", "type": "object", "properties": { - "fdc3Version": { - "title": "FDC3 version", + "implementationDetails": { + "title": "Implementation Details", "type": "string", - "description": "The version of FDC3 API that the Desktop Agent proxy is providing support for." + "description": "Details about the UI implementation in the iframe, such as vendor and version, for logging purposes." + }, + "initialCSS": { + "title": "Initial CSS", + "type": "object", + "description": "A constrained set of CSS properties that should be set on the iframe before it is displayed.", + "properties": { + "height": {"type": "string", "title": "height", "description": "The initial height of the iframe"}, + "width": {"type": "string", "title": "width", "description": "The initial width of the iframe"}, + "position": {"type": "string", "title": "position", "description": "The initial position CSS to apply to the iframe"}, + "zIndex": {"type": "string", "title": "zIndex", "description": "The initial zindex to apply to the iframe"}, + "left": {"type": "string", "title": "left", "description": "The initial left property to apply to the iframe"}, + "top": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, + "transition": {"type": "string", "title": "transition", "description": "The transition property to apply to the iframe"}, + "maxHeight": {"type": "string", "title": "maxHeight", "description": "The maximum height to apply to the iframe"}, + "maxWidth": {"type": "string", "title": "maxWidth", "description": "The maximum with to apply to the iframe"} + }, + "required": ["height", "width", "position", "top", "left"] } }, "additionalProperties": false, - "required": ["fdc3Version"] + "required": ["implementationDetails","initialCSS"] } }, "required": [ diff --git a/schemas/api/iFrameMessage.schema.json b/schemas/api/iFrameMessage.schema.json index aaad9b99b..9e5bc5256 100644 --- a/schemas/api/iFrameMessage.schema.json +++ b/schemas/api/iFrameMessage.schema.json @@ -11,12 +11,12 @@ "enum": [ "iframeHello", "iframeHandshake", + "iframeRestyle", + "iframeDrag", "iframeResolve", "iframeResolveAction", "iframeChannels", - "iframeChannelSelected", - "iframeChannelResize", - "iframeChannelDrag" + "iframeChannelSelected" ], "description": "Identifies the type of the message to or from the iframe." }, diff --git a/schemas/api/iFrameRestyle.schema.json b/schemas/api/iFrameRestyle.schema.json new file mode 100644 index 000000000..9ed06f423 --- /dev/null +++ b/schemas/api/iFrameRestyle.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/iframeRestyle.schema.json", + "title": "iframe Restyle", + "description": "Message from a UI iframe to the DA proxy code (setup by `getAgent()`) with updated styling information to apply to the iframe. Can be used to implement a pop-open or close interaction or other transition needed by a UI implementation.", + "type": "object", + "allOf": [ + { + "$ref": "#/$defs/IframeRestyleBase" + }, + { + "$ref": "iFrameMessage.schema.json" + } + ], + "$defs": { + "IframeRestyleBase": { + "type": "object", + "properties": { + "type": { + "title": "iframeRestyle Message Type", + "const": "iframeRestyle" + }, + "payload": { + "title": "iframeRestyle Payload", + "type": "object", + "properties": { + "updatedCSS": { + "title": "Updated CSS", + "type": "object", + "description": "A constrained set of CSS properties that should be applied to the iframe.", + "properties": { + "height": {"type": "string", "title": "height", "description": "The updated height of the iframe"}, + "width": {"type": "string", "title": "width", "description": "The updated width of the iframe"}, + "position": {"type": "string", "title": "position", "description": "The updated position CSS to apply to the iframe"}, + "zIndex": {"type": "string", "title": "zIndex", "description": "The updated zindex to apply to the iframe"}, + "left": {"type": "string", "title": "left", "description": "The updated left property to apply to the iframe"}, + "top": {"type": "string", "title": "top", "description": "The updated right property to apply to the iframe"}, + "transition": {"type": "string", "title": "transition", "description": "The updated transition property to apply to the iframe"}, + "maxHeight": {"type": "string", "title": "maxHeight", "description": "The updated maximum height to apply to the iframe"}, + "maxWidth": {"type": "string", "title": "maxWidth", "description": "The updated maximum with to apply to the iframe"} + }, + "required": [] + }, + "resizeAnchor": { + "title": "Resizing", + "description": "Optional property to support resizing. When resizing anchor at the indicated location (without top or left set),\ne.g.\n\t- for top-left and a larger size: the bottom right corner should move down and out.\n\t- for top and smaller size: both the bottom corners should move in and up.", + "anyOf": [ + { "type": "string", "const": "top-left" }, + { "type": "string", "const": "top" }, + { "type": "string", "const": "top-right" }, + { "type": "string", "const": "right" }, + { "type": "string", "const": "bottom-right" }, + { "type": "string", "const": "bottom" }, + { "type": "string", "const": "bottom-left" }, + { "type": "string", "const": "left" }, + { "type": "string", "const": "center" } + ] + } + }, + "additionalProperties": false, + "required": ["updatedCSS"] + } + }, + "required": [ + "type", + "payload" + ], + "additionalProperties": false + } + } +} + diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index ad2fc587b..56b052e64 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannelDrag, IframeChannelResize, IframeChannels, IframeChannelSelected, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -40,15 +40,15 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); -// const iframeChannelDrag = Convert.toIframeChannelDrag(json); -// const iframeChannelResize = Convert.toIframeChannelResize(json); // const iframeChannels = Convert.toIframeChannels(json); // const iframeChannelSelected = Convert.toIframeChannelSelected(json); +// const iframeDrag = Convert.toIframeDrag(json); // const iframeHandshake = Convert.toIframeHandshake(json); // const iframeHello = Convert.toIframeHello(json); // const iframeMessage = Convert.toIframeMessage(json); // const iframeResolve = Convert.toIframeResolve(json); // const iframeResolveAction = Convert.toIframeResolveAction(json); +// const iframeRestyle = Convert.toIframeRestyle(json); // const intentEvent = Convert.toIntentEvent(json); // const intentListenerUnsubscribeRequest = Convert.toIntentListenerUnsubscribeRequest(json); // const intentListenerUnsubscribeResponse = Convert.toIntentListenerUnsubscribeResponse(json); @@ -2015,40 +2015,37 @@ export interface GetUserChannelsResponsePayload { */ /** - * Message from the channel selector UI to the DA proxy when the user drags the selector to - * a new location. + * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an + * iframe with the channel definitions and current channel selection. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in * either direction. */ -export interface IframeChannelDrag { +export interface IframeChannels { /** * The message payload */ - payload: IframeChannelDragPayload; + payload: IframeChannelsPayload; /** * Identifies the type of the message to or from the iframe. */ - type: "iframeChannelDrag"; + type: "iframeChannels"; } /** * The message payload */ -export interface IframeChannelDragPayload { +export interface IframeChannelsPayload { /** - * The offset to move the frame by + * The id of the channel that should be currently selected, or `null` if none should be + * selected */ - mouse: MouseClass; -} - -/** - * The offset to move the frame by - */ -export interface MouseClass { - offsetX: number; - offsetY: number; + selected: null | string; + /** + * User Channel definitions + */ + userChannels: Channel[]; } /** @@ -2056,106 +2053,74 @@ export interface MouseClass { */ /** - * Message from the channel selector UI to the DA proxy when the user hits a toggle button - * that opens or closes the selector or otherwise resizes it. Includes the size that it - * should change to and the corner (if any) at which the change should be made. + * Message from the channel selector UI to the DA proxy sent when the channel selection + * changes. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in * either direction. */ -export interface IframeChannelResize { +export interface IframeChannelSelected { /** * The message payload */ - payload: IframeChannelResizePayload; + payload: IframeChannelSelectedPayload; /** * Identifies the type of the message to or from the iframe. */ - type: "iframeChannelResize"; + type: "iframeChannelSelected"; } /** * The message payload */ -export interface IframeChannelResizePayload { - /** - * The updated dimensions of the UI - */ - dimensions: DimensionsClass; +export interface IframeChannelSelectedPayload { /** - * When resizing anchor at the indicated location, - * e.g. - * - for top-left and a larger size: the bottom right corner should move down and out. - * - for top and smaller size: both the bottom corners should move in and up. + * The id of the channel that should be currently selected, or `null` if none should be + * selected */ - resizeAnchor: Resizing; -} - -/** - * The updated dimensions of the UI - */ -export interface DimensionsClass { - height: number; - width: number; + selected: null | string; } -/** - * When resizing anchor at the indicated location, - * e.g. - * - for top-left and a larger size: the bottom right corner should move down and out. - * - for top and smaller size: both the bottom corners should move in and up. - */ -export type Resizing = "top-left" | "top" | "top-right" | "right" | "bottom-right" | "bottom" | "bottom-left" | "left" | "center"; - /** * Identifies the type of the message to or from the iframe. */ /** - * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an - * iframe with the channel definitions and current channel selection. + * Message from a UI iframe to the DA proxy (setup by `getAgent()`) indicating that the user + * is dragging the UI to a new location and providing the offset to apply to the location. + * The DA proxy implementation should limit the location to the current bounds of the + * window's viewport * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in * either direction. */ -export interface IframeChannels { +export interface IframeDrag { /** * The message payload */ - payload: IframeChannelsPayload; + payload: IframeDragPayload; /** * Identifies the type of the message to or from the iframe. */ - type: "iframeChannels"; + type: "iframeDrag"; } /** * The message payload */ -export interface IframeChannelsPayload { +export interface IframeDragPayload { /** - * If the channel selector was previously displayed in this window its location may be - * restored by setting the location coordinates - */ - location?: Location; - /** - * The id of the channel that should be currently selected, or `null` if none should be - * selected - */ - selected: null | string; - /** - * User Channel definitions + * The offset to move the frame by */ - userChannels: Channel[]; + mouseOffsets: MouseOffsets; } /** - * If the channel selector was previously displayed in this window its location may be - * restored by setting the location coordinates + * The offset to move the frame by */ -export interface Location { +export interface MouseOffsets { x: number; y: number; } @@ -2165,33 +2130,32 @@ export interface Location { */ /** - * Message from the channel selector UI to the DA proxy sent when the channel selection - * changes. + * Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) + * with a MessagePort appended over further communication is conducted. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in * either direction. */ -export interface IframeChannelSelected { +export interface IframeHandshake { /** * The message payload */ - payload: IframeChannelSelectedPayload; + payload: IframeHandshakePayload; /** * Identifies the type of the message to or from the iframe. */ - type: "iframeChannelSelected"; + type: "iframeHandshake"; } /** * The message payload */ -export interface IframeChannelSelectedPayload { +export interface IframeHandshakePayload { /** - * The id of the channel that should be currently selected, or `null` if none should be - * selected + * The version of FDC3 API that the Desktop Agent will provide support for. */ - selected: null | string; + fdc3Version: string; } /** @@ -2199,67 +2163,82 @@ export interface IframeChannelSelectedPayload { */ /** - * Handshake message sent back by an iframe to the DA proxy code indicating that it is setup - * and ready to communicate over the MessagePort. + * Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to + * indicate it is ready to communicate and containing initial CSS to set on the iframe. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in * either direction. */ -export interface IframeHandshake { +export interface IframeHello { /** * The message payload */ - payload: IframeHandshakePayload; + payload: IframeHelloPayload; /** * Identifies the type of the message to or from the iframe. */ - type: "iframeHandshake"; + type: "iframeHello"; } /** * The message payload */ -export interface IframeHandshakePayload { +export interface IframeHelloPayload { /** * Details about the UI implementation in the iframe, such as vendor and version, for * logging purposes. */ implementationDetails: string; + /** + * A constrained set of CSS properties that should be set on the iframe before it is + * displayed. + */ + initialCSS: InitialCSS; } /** - * Identifies the type of the message to or from the iframe. - */ - -/** - * Hello message sent by the DA proxy code in getAgent() to an iframe, that it has injected - * into the page, with a MessagePort appended that should be used for subsequent - * communication steps. - * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A constrained set of CSS properties that should be set on the iframe before it is + * displayed. */ -export interface IframeHello { +export interface InitialCSS { /** - * The message payload + * The initial height of the iframe */ - payload: IframeHelloPayload; + height: string; /** - * Identifies the type of the message to or from the iframe. + * The initial left property to apply to the iframe */ - type: "iframeHello"; -} - -/** - * The message payload - */ -export interface IframeHelloPayload { + left: string; /** - * The version of FDC3 API that the Desktop Agent proxy is providing support for. + * The maximum height to apply to the iframe */ - fdc3Version: string; + maxHeight?: string; + /** + * The maximum with to apply to the iframe + */ + maxWidth?: string; + /** + * The initial position CSS to apply to the iframe + */ + position: string; + /** + * The initial right property to apply to the iframe + */ + top: string; + /** + * The transition property to apply to the iframe + */ + transition?: string; + /** + * The initial width of the iframe + */ + width: string; + /** + * The initial zindex to apply to the iframe + */ + zIndex?: string; + [property: string]: any; } /** @@ -2285,7 +2264,7 @@ export interface IframeMessage { /** * Identifies the type of the message to or from the iframe. */ -export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected" | "iframeChannelResize" | "iframeChannelDrag"; +export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeRestyle" | "iframeDrag" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected"; /** * Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an @@ -2357,6 +2336,100 @@ export interface IframeResolveActionPayload { export type Action = "hover" | "click" | "cancel"; +/** + * Identifies the type of the message to or from the iframe. + */ + +/** + * Message from a UI iframe to the DA proxy code (setup by `getAgent()`) with updated + * styling information to apply to the iframe. Can be used to implement a pop-open or close + * interaction or other transition needed by a UI implementation. + * + * A message used to communicate with iframes injected by `getAgent()` for displaying UI + * elements such as the intent resolver or channel selector. Used for messages sent in + * either direction. + */ +export interface IframeRestyle { + /** + * The message payload + */ + payload: IframeRestylePayload; + /** + * Identifies the type of the message to or from the iframe. + */ + type: "iframeRestyle"; +} + +/** + * The message payload + */ +export interface IframeRestylePayload { + /** + * Optional property to support resizing. When resizing anchor at the indicated location + * (without top or left set), + * e.g. + * - for top-left and a larger size: the bottom right corner should move down and out. + * - for top and smaller size: both the bottom corners should move in and up. + */ + resizeAnchor?: Resizing; + /** + * A constrained set of CSS properties that should be applied to the iframe. + */ + updatedCSS: UpdatedCSS; +} + +/** + * Optional property to support resizing. When resizing anchor at the indicated location + * (without top or left set), + * e.g. + * - for top-left and a larger size: the bottom right corner should move down and out. + * - for top and smaller size: both the bottom corners should move in and up. + */ +export type Resizing = "top-left" | "top" | "top-right" | "right" | "bottom-right" | "bottom" | "bottom-left" | "left" | "center"; + +/** + * A constrained set of CSS properties that should be applied to the iframe. + */ +export interface UpdatedCSS { + /** + * The updated height of the iframe + */ + height?: string; + /** + * The updated left property to apply to the iframe + */ + left?: string; + /** + * The updated maximum height to apply to the iframe + */ + maxHeight?: string; + /** + * The updated maximum with to apply to the iframe + */ + maxWidth?: string; + /** + * The updated position CSS to apply to the iframe + */ + position?: string; + /** + * The updated right property to apply to the iframe + */ + top?: string; + /** + * The updated transition property to apply to the iframe + */ + transition?: string; + /** + * The updated width of the iframe + */ + width?: string; + /** + * The updated zindex to apply to the iframe + */ + zIndex?: string; + [property: string]: any; +} + /** * Identifies the type of the message to or from the iframe. */ @@ -4007,22 +4080,6 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } - public static toIframeChannelDrag(json: string): IframeChannelDrag { - return cast(JSON.parse(json), r("IframeChannelDrag")); - } - - public static iframeChannelDragToJson(value: IframeChannelDrag): string { - return JSON.stringify(uncast(value, r("IframeChannelDrag")), null, 2); - } - - public static toIframeChannelResize(json: string): IframeChannelResize { - return cast(JSON.parse(json), r("IframeChannelResize")); - } - - public static iframeChannelResizeToJson(value: IframeChannelResize): string { - return JSON.stringify(uncast(value, r("IframeChannelResize")), null, 2); - } - public static toIframeChannels(json: string): IframeChannels { return cast(JSON.parse(json), r("IframeChannels")); } @@ -4039,6 +4096,14 @@ export class Convert { return JSON.stringify(uncast(value, r("IframeChannelSelected")), null, 2); } + public static toIframeDrag(json: string): IframeDrag { + return cast(JSON.parse(json), r("IframeDrag")); + } + + public static iframeDragToJson(value: IframeDrag): string { + return JSON.stringify(uncast(value, r("IframeDrag")), null, 2); + } + public static toIframeHandshake(json: string): IframeHandshake { return cast(JSON.parse(json), r("IframeHandshake")); } @@ -4079,6 +4144,14 @@ export class Convert { return JSON.stringify(uncast(value, r("IframeResolveAction")), null, 2); } + public static toIframeRestyle(json: string): IframeRestyle { + return cast(JSON.parse(json), r("IframeRestyle")); + } + + public static iframeRestyleToJson(value: IframeRestyle): string { + return JSON.stringify(uncast(value, r("IframeRestyle")), null, 2); + } + public static toIntentEvent(json: string): IntentEvent { return cast(JSON.parse(json), r("IntentEvent")); } @@ -4905,42 +4978,14 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), - "IframeChannelDrag": o([ - { json: "payload", js: "payload", typ: r("IframeChannelDragPayload") }, - { json: "type", js: "type", typ: r("IframeChannelDragType") }, - ], false), - "IframeChannelDragPayload": o([ - { json: "mouse", js: "mouse", typ: r("MouseClass") }, - ], false), - "MouseClass": o([ - { json: "offsetX", js: "offsetX", typ: 0 }, - { json: "offsetY", js: "offsetY", typ: 0 }, - ], false), - "IframeChannelResize": o([ - { json: "payload", js: "payload", typ: r("IframeChannelResizePayload") }, - { json: "type", js: "type", typ: r("IframeChannelResizeType") }, - ], false), - "IframeChannelResizePayload": o([ - { json: "dimensions", js: "dimensions", typ: r("DimensionsClass") }, - { json: "resizeAnchor", js: "resizeAnchor", typ: r("Resizing") }, - ], false), - "DimensionsClass": o([ - { json: "height", js: "height", typ: 0 }, - { json: "width", js: "width", typ: 0 }, - ], false), "IframeChannels": o([ { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, { json: "type", js: "type", typ: r("IframeChannelsType") }, ], false), "IframeChannelsPayload": o([ - { json: "location", js: "location", typ: u(undefined, r("Location")) }, { json: "selected", js: "selected", typ: u(null, "") }, { json: "userChannels", js: "userChannels", typ: a(r("Channel")) }, ], false), - "Location": o([ - { json: "x", js: "x", typ: 0 }, - { json: "y", js: "y", typ: 0 }, - ], false), "IframeChannelSelected": o([ { json: "payload", js: "payload", typ: r("IframeChannelSelectedPayload") }, { json: "type", js: "type", typ: r("IframeChannelSelectedType") }, @@ -4948,20 +4993,43 @@ const typeMap: any = { "IframeChannelSelectedPayload": o([ { json: "selected", js: "selected", typ: u(null, "") }, ], false), + "IframeDrag": o([ + { json: "payload", js: "payload", typ: r("IframeDragPayload") }, + { json: "type", js: "type", typ: r("IframeDragType") }, + ], false), + "IframeDragPayload": o([ + { json: "mouseOffsets", js: "mouseOffsets", typ: r("MouseOffsets") }, + ], false), + "MouseOffsets": o([ + { json: "x", js: "x", typ: 0 }, + { json: "y", js: "y", typ: 0 }, + ], false), "IframeHandshake": o([ { json: "payload", js: "payload", typ: r("IframeHandshakePayload") }, { json: "type", js: "type", typ: r("IframeHandshakeType") }, ], false), "IframeHandshakePayload": o([ - { json: "implementationDetails", js: "implementationDetails", typ: "" }, + { json: "fdc3Version", js: "fdc3Version", typ: "" }, ], false), "IframeHello": o([ { json: "payload", js: "payload", typ: r("IframeHelloPayload") }, { json: "type", js: "type", typ: r("IframeHelloType") }, ], false), "IframeHelloPayload": o([ - { json: "fdc3Version", js: "fdc3Version", typ: "" }, - ], false), + { json: "implementationDetails", js: "implementationDetails", typ: "" }, + { json: "initialCSS", js: "initialCSS", typ: r("InitialCSS") }, + ], false), + "InitialCSS": o([ + { json: "height", js: "height", typ: "" }, + { json: "left", js: "left", typ: "" }, + { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, + { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, + { json: "position", js: "position", typ: "" }, + { json: "top", js: "top", typ: "" }, + { json: "transition", js: "transition", typ: u(undefined, "") }, + { json: "width", js: "width", typ: "" }, + { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, + ], "any"), "IframeMessage": o([ { json: "payload", js: "payload", typ: u(undefined, m("any")) }, { json: "type", js: "type", typ: r("IframeMessageType") }, @@ -4983,6 +5051,25 @@ const typeMap: any = { { json: "appIdentifier", js: "appIdentifier", typ: u(undefined, r("AppIdentifier")) }, { json: "intent", js: "intent", typ: u(undefined, "") }, ], false), + "IframeRestyle": o([ + { json: "payload", js: "payload", typ: r("IframeRestylePayload") }, + { json: "type", js: "type", typ: r("IframeRestyleType") }, + ], false), + "IframeRestylePayload": o([ + { json: "resizeAnchor", js: "resizeAnchor", typ: u(undefined, r("Resizing")) }, + { json: "updatedCSS", js: "updatedCSS", typ: r("UpdatedCSS") }, + ], false), + "UpdatedCSS": o([ + { json: "height", js: "height", typ: u(undefined, "") }, + { json: "left", js: "left", typ: u(undefined, "") }, + { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, + { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, + { json: "position", js: "position", typ: u(undefined, "") }, + { json: "top", js: "top", typ: u(undefined, "") }, + { json: "transition", js: "transition", typ: u(undefined, "") }, + { json: "width", js: "width", typ: u(undefined, "") }, + { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, + ], "any"), "IntentEvent": o([ { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("IntentEventPayload") }, @@ -5507,29 +5594,15 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], - "IframeChannelDragType": [ - "iframeChannelDrag", - ], - "Resizing": [ - "bottom", - "bottom-left", - "bottom-right", - "center", - "left", - "right", - "top", - "top-left", - "top-right", - ], - "IframeChannelResizeType": [ - "iframeChannelResize", - ], "IframeChannelsType": [ "iframeChannels", ], "IframeChannelSelectedType": [ "iframeChannelSelected", ], + "IframeDragType": [ + "iframeDrag", + ], "IframeHandshakeType": [ "iframeHandshake", ], @@ -5537,14 +5610,14 @@ const typeMap: any = { "iframeHello", ], "IframeMessageType": [ - "iframeChannelDrag", - "iframeChannelResize", "iframeChannelSelected", "iframeChannels", + "iframeDrag", "iframeHandshake", "iframeHello", "iframeResolve", "iframeResolveAction", + "iframeRestyle", ], "IframeResolveType": [ "iframeResolve", @@ -5557,6 +5630,20 @@ const typeMap: any = { "IframeResolveActionType": [ "iframeResolveAction", ], + "Resizing": [ + "bottom", + "bottom-left", + "bottom-right", + "center", + "left", + "right", + "top", + "top-left", + "top-right", + ], + "IframeRestyleType": [ + "iframeRestyle", + ], "IntentEventType": [ "intentEvent", ], From 0dbdf24737172ea8a00b52384b2be6a9c619c3e8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 29 Aug 2024 12:04:57 +0100 Subject: [PATCH 067/152] Adding heartbeat event and responses to DACP --- schemas/api/agentEvent.schema.json | 7 +- schemas/api/appRequest.schema.json | 1 + schemas/api/heartbeatEvent.schema.json | 45 +++++++ .../api/heartbeatResponseRequest.schema.json | 47 +++++++ src/api/BrowserTypes.ts | 123 +++++++++++++++++- 5 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 schemas/api/heartbeatEvent.schema.json create mode 100644 schemas/api/heartbeatResponseRequest.schema.json diff --git a/schemas/api/agentEvent.schema.json b/schemas/api/agentEvent.schema.json index 9c7b46bdd..58b16dccc 100644 --- a/schemas/api/agentEvent.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -9,13 +9,14 @@ "title": "Event Message Type", "type": "string", "enum": [ - "intentEvent", + "addEventListenerEvent", "broadcastEvent", "channelChangedEvent", + "heartbeatEvent", + "intentEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", - "privateChannelOnUnsubscribeEvent", - "addEventListenerEvent" + "privateChannelOnUnsubscribeEvent" ], "description": "Identifies the type of the message and it is typically set to the FDC3 function name that the message relates to, e.g. 'findIntent', with 'Response' appended." }, diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index 72ea343fd..cb4a25229 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -25,6 +25,7 @@ "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", + "heartbeatResponseRequest", "intentListenerUnsubscribeRequest", "intentResultRequest", "joinUserChannelRequest", diff --git a/schemas/api/heartbeatEvent.schema.json b/schemas/api/heartbeatEvent.schema.json new file mode 100644 index 000000000..e88ef7f3c --- /dev/null +++ b/schemas/api/heartbeatEvent.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatEvent.schema.json", + "type": "object", + "title": "Heartbeat Event", + "description": "A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is alive and that the application should send a heartbeatResponseRequest to the agent in response.", + "allOf": [ + { + "$ref": "agentEvent.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/HeartbeatEventType" + }, + "payload": { + "$ref": "#/$defs/HeartbeatEventPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "HeartbeatEventType": { + "title": "Heartbeat Event Message Type", + "const": "heartbeatEvent" + }, + "HeartbeatEventPayload": { + "title": "heartbeat Event Payload", + "type": "object", + "properties": { + "timestamp": { + "title": "Timestamp", + "description": "The time at which the heartbeat event was sent, which should be quoted in the response", + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false, + "required": ["timestamp"] + } + } +} \ No newline at end of file diff --git a/schemas/api/heartbeatResponseRequest.schema.json b/schemas/api/heartbeatResponseRequest.schema.json new file mode 100644 index 000000000..49b8aad67 --- /dev/null +++ b/schemas/api/heartbeatResponseRequest.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatResponseRequest.schema.json", + "type": "object", + "title": "HeartbeatResponse Request", + "description": "A request that serves as a response to a heartbeat event from the Desktop Agent and indicates that an application window or frame is still alive.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/HeartbeatResponseRequestType" + }, + "payload": { + "$ref": "#/$defs/HeartbeatResponseRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "HeartbeatResponseRequestType": { + "title": "HeartbeatResponse Request Message Type", + "const": "heartbeatResponseRequest" + }, + "HeartbeatResponseRequestPayload": { + "title": "heartbeatResponse Request Payload", + "type": "object", + "properties": { + "timestamp": { + "title": "Timestamp", + "description": "The timestamp of the heartbeatEvent that is being responded to.", + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false, + "required": [ + "timestamp" + ] + } + } +} \ No newline at end of file diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 56b052e64..b2fd1568d 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatEvent, HeartbeatResponseRequest, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -40,6 +40,8 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const heartbeatEvent = Convert.toHeartbeatEvent(json); +// const heartbeatResponseRequest = Convert.toHeartbeatResponseRequest(json); // const iframeChannels = Convert.toIframeChannels(json); // const iframeChannelSelected = Convert.toIframeChannelSelected(json); // const iframeDrag = Convert.toIframeDrag(json); @@ -524,7 +526,7 @@ export interface AgentEventMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export type EventMessageType = "intentEvent" | "broadcastEvent" | "channelChangedEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent" | "addEventListenerEvent"; +export type EventMessageType = "addEventListenerEvent" | "broadcastEvent" | "channelChangedEvent" | "heartbeatEvent" | "intentEvent" | "privateChannelOnAddContextListenerEvent" | "privateChannelOnDisconnectEvent" | "privateChannelOnUnsubscribeEvent"; /** * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the @@ -616,7 +618,7 @@ export interface AppRequestMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "eventListenerUnsubscribeRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "eventListenerUnsubscribeRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "heartbeatResponseRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -2014,6 +2016,81 @@ export interface GetUserChannelsResponsePayload { * the message relates to, e.g. 'findIntent', with 'Response' appended. */ +/** + * A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is + * alive and that the application should send a heartbeatResponseRequest to the agent in + * response. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface HeartbeatEvent { + /** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ + meta: AddEventListenerEventMeta; + /** + * The message payload contains details of the event that the app is being notified about. + */ + payload: HeartbeatEventPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + type: "heartbeatEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface HeartbeatEventPayload { + /** + * The time at which the heartbeat event was sent, which should be quoted in the response + */ + timestamp: Date; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. + */ + +/** + * A request that serves as a response to a heartbeat event from the Desktop Agent and + * indicates that an application window or frame is still alive. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface HeartbeatResponseRequest { + /** + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. + */ + payload: HeartbeatResponseRequestPayload; + /** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + type: "heartbeatResponseRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface HeartbeatResponseRequestPayload { + /** + * The timestamp of the heartbeatEvent that is being responded to. + */ + timestamp: Date; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + /** * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an * iframe with the channel definitions and current channel selection. @@ -4080,6 +4157,22 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } + public static toHeartbeatEvent(json: string): HeartbeatEvent { + return cast(JSON.parse(json), r("HeartbeatEvent")); + } + + public static heartbeatEventToJson(value: HeartbeatEvent): string { + return JSON.stringify(uncast(value, r("HeartbeatEvent")), null, 2); + } + + public static toHeartbeatResponseRequest(json: string): HeartbeatResponseRequest { + return cast(JSON.parse(json), r("HeartbeatResponseRequest")); + } + + public static heartbeatResponseRequestToJson(value: HeartbeatResponseRequest): string { + return JSON.stringify(uncast(value, r("HeartbeatResponseRequest")), null, 2); + } + public static toIframeChannels(json: string): IframeChannels { return cast(JSON.parse(json), r("IframeChannels")); } @@ -4978,6 +5071,22 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), + "HeartbeatEvent": o([ + { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "payload", js: "payload", typ: r("HeartbeatEventPayload") }, + { json: "type", js: "type", typ: r("HeartbeatEventType") }, + ], false), + "HeartbeatEventPayload": o([ + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), + "HeartbeatResponseRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("HeartbeatResponseRequestPayload") }, + { json: "type", js: "type", typ: r("HeartbeatResponseRequestType") }, + ], false), + "HeartbeatResponseRequestPayload": o([ + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), "IframeChannels": o([ { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, { json: "type", js: "type", typ: r("IframeChannelsType") }, @@ -5428,6 +5537,7 @@ const typeMap: any = { "addEventListenerEvent", "broadcastEvent", "channelChangedEvent", + "heartbeatEvent", "intentEvent", "privateChannelOnAddContextListenerEvent", "privateChannelOnDisconnectEvent", @@ -5479,6 +5589,7 @@ const typeMap: any = { "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", + "heartbeatResponseRequest", "intentListenerUnsubscribeRequest", "intentResultRequest", "joinUserChannelRequest", @@ -5594,6 +5705,12 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], + "HeartbeatEventType": [ + "heartbeatEvent", + ], + "HeartbeatResponseRequestType": [ + "heartbeatResponseRequest", + ], "IframeChannelsType": [ "iframeChannels", ], From a91951c94089a4da50ebbd1e6ef21df9ac493854 Mon Sep 17 00:00:00 2001 From: Rob Moffat Date: Thu, 29 Aug 2024 12:16:28 +0100 Subject: [PATCH 068/152] Update docs/api/ref/GetAgent.md Co-authored-by: Kris West --- docs/api/ref/GetAgent.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 243ee8576..0f7e13c5a 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -18,7 +18,7 @@ import { getAgent, DesktopAgent } from "@finos/fdc3"; try { const fdc3: DesktopAgent = await getAgent(); //do FDC3 things here -} catch (e) { +} catch (e: AgentError) { // Failed to connect } ``` From c0423098ee1d2f730cb333b64ba9c810e6a72f8e Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 30 Aug 2024 10:44:23 +0100 Subject: [PATCH 069/152] Rename `heartbeatResponseRequest` to `heartbestAcknowledgment` --- .../api/heartbeatAcknowledgment.schema.json | 47 +++++++++++++++++++ .../api/heartbeatResponseRequest.schema.json | 47 ------------------- 2 files changed, 47 insertions(+), 47 deletions(-) create mode 100644 schemas/api/heartbeatAcknowledgment.schema.json delete mode 100644 schemas/api/heartbeatResponseRequest.schema.json diff --git a/schemas/api/heartbeatAcknowledgment.schema.json b/schemas/api/heartbeatAcknowledgment.schema.json new file mode 100644 index 000000000..b5ce3f5f8 --- /dev/null +++ b/schemas/api/heartbeatAcknowledgment.schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgement.schema.json", + "type": "object", + "title": "HeartbeatAcknowledgement Request", + "description": "A request that serves as an acknowledgement of a heartbeat event from the Desktop Agent and indicates that an application window or frame is still alive.", + "allOf": [ + { + "$ref": "appRequest.schema.json" + }, + { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/HeartbeatAcknowledgementRequestType" + }, + "payload": { + "$ref": "#/$defs/HeartbeatAcknowledgementRequestPayload" + }, + "meta": true + }, + "additionalProperties": false + } + ], + "$defs": { + "HeartbeatAcknowledgementRequestType": { + "title": "HeartbeatAcknowledgement Request Message Type", + "const": "heartbeatAcknowledgementRequest" + }, + "HeartbeatAcknowledgementRequestPayload": { + "title": "heartbeatAcknowledgement Request Payload", + "type": "object", + "properties": { + "timestamp": { + "title": "Timestamp", + "description": "The timestamp of the heartbeatEvent that is being acknowledged.", + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false, + "required": [ + "timestamp" + ] + } + } +} \ No newline at end of file diff --git a/schemas/api/heartbeatResponseRequest.schema.json b/schemas/api/heartbeatResponseRequest.schema.json deleted file mode 100644 index 49b8aad67..000000000 --- a/schemas/api/heartbeatResponseRequest.schema.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatResponseRequest.schema.json", - "type": "object", - "title": "HeartbeatResponse Request", - "description": "A request that serves as a response to a heartbeat event from the Desktop Agent and indicates that an application window or frame is still alive.", - "allOf": [ - { - "$ref": "appRequest.schema.json" - }, - { - "type": "object", - "properties": { - "type": { - "$ref": "#/$defs/HeartbeatResponseRequestType" - }, - "payload": { - "$ref": "#/$defs/HeartbeatResponseRequestPayload" - }, - "meta": true - }, - "additionalProperties": false - } - ], - "$defs": { - "HeartbeatResponseRequestType": { - "title": "HeartbeatResponse Request Message Type", - "const": "heartbeatResponseRequest" - }, - "HeartbeatResponseRequestPayload": { - "title": "heartbeatResponse Request Payload", - "type": "object", - "properties": { - "timestamp": { - "title": "Timestamp", - "description": "The timestamp of the heartbeatEvent that is being responded to.", - "type": "string", - "format": "date-time" - } - }, - "additionalProperties": false, - "required": [ - "timestamp" - ] - } - } -} \ No newline at end of file From 522319a7959f3b41e4b56e5f0ad2495eaa87d862 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 2 Sep 2024 16:58:20 +0100 Subject: [PATCH 070/152] WIP DA specifications --- docs/api/spec.md | 7 +- .../api/specs/browserResidentDesktopAgents.md | 292 +++++++++--------- docs/api/specs/preloadDesktopAgents.md | 41 +-- 3 files changed, 152 insertions(+), 188 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 38eec38f5..4159ebdae 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -48,10 +48,9 @@ The FDC3 API specification consists of interfaces. It is expected that each Des - [`Channel`](ref/Channel) - [`PrivateChannel`](ref/PrivateChannel) - [`Listener`](ref/Types#listener) +- [Utility types](ref/Types#listener) and [Metadata Objects](ref/Metadata). -Other interfaces defined in the spec are not critical to define as concrete types. Rather, the Desktop Agent should expect to have objects of the interface shape passed into or out of their library. - -The means to access an API interface is defined separately for each language in which FDC3 is implemented. These definitions are important as they affect whether applicaitons can be written in a vendor agnostic format so that they run under any Standards-conformant implementation. For details of how to access an API interface in particular languages please see [Supported Platforms](supported-platforms). +The means to access the main FDC3 API interface (a `DesktopAgent` implementation) is defined separately for each language in which FDC3 is implemented. These definitions are important as they affect whether applications can be written in a vendor agnostic format so that they run under any Standards-conformant implementation. ### Implementation language @@ -59,7 +58,7 @@ FDC3 and the Desktop Agent API it defines are intended to be independent of part Specifically, the use of ['unions'](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) of primitive values in API type and metadata objects, or function parameters SHOULD be avoided as they often cannot be replicated in other languages. Unions of more complex types (such as specific [Context](ref/Context) Types) may be used where a suitable interface is available or can be created to allow the required polymorphism in languages other than TypeScript. -For implementation details relating to particular languages, and how to access the API in those languages, please see [Supported Platforms](supported-platforms). +For implementation details relating to particular languages and how to access the API in those languages please see [Supported Platforms](supported-platforms). ### Standards vs. Implementation diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 7bd8f78cb..2a6655dcc 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -4,23 +4,31 @@ sidebar_label: Browser Desktop Agents title: Browser Desktop Agents (next) --- -This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by FINOS. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView or Browser Extension based implementations.) +:::info _[@experimental](../fdc3-compliance#experimental-features)_ -This specification only applies to apps running in a browser and therefore assumes use of JavaScript and HTML APIs. Implementations in other languages such as .NET are not covered. Along with this specification, a new general connection strategy has been established for _all_ FDC3 compliant applications: FDC3 compliant apps SHOULD make use of the `@finos/fdc3` library to establish their FDC3 interface (a `DesktopAgent` object instance). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or Preload DAs without code modification. We refer to this concept as Write Once Run Anywhere (WORA). +Browser Resident Desktop Agents are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in the future version and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. + +::: + +This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by the [`@finos.fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) and a standardized communication protocol. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView2 or a browser-extension based implementation). + +This specification only applies to apps running in a browser and therefore assumes use of JavaScript/TypeScript and HTML APIs. Implementations in other languages such as .NET are not covered. + +Along with this specification, a new general connection strategy has been established for FDC3 compliant web-applications: FDC3 compliant apps SHOULD make use of `getAent()` function provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) to retrieve their FDC3 interface (an instance of an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or [Preload DAs](./preloadDesktopAgents) without code modification. We refer to this concept as Write Once Run Anywhere (WORA). :::info -Prior to FDC3 2.2, only Preload DAs were supported. +Prior to FDC3 2.2, only [Preload Desktop Agents](./preloadDesktopAgents) were supported. ::: :::note -This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the `@finos/fdc3` library. Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates. +This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3). Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates. ::: -:::note +:::tip When referencing "DA" in this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. @@ -28,184 +36,160 @@ When referencing "DA" in this document we will hereafter always mean a "Browser- ## Launching apps -As a prerequisite for running FDC3 in the browser, a DA must first exist as running code in a browser window (See failover functions for an exception to this rule). We will refer to this window as the "DA Window". +As a prerequisite for running FDC3 in the browser, a DA must first exist as running code in a browser window (See failover functions for an exception to this rule), although that code MAY also connect to or rely on remotely hosted services. We will refer to this window as the "DA Window". -> Note - It is possible to have multiple DA Windows. For instance, a DA may propagate itself into new windows. Communications between DA Windows is an implementation detail. +As the DA typically acts as a launcher for applications, it will often be the case that the DA window is related to the application window(s) in that it may have create the application window with `window.open()` or by creating an iframe and loading the application URL into it. Hence, the DA window may be referred to as a 'parent' (window or frame) of the application frame and the relationship may be used to implement communication between the frames. -When an app runs `getAgent()`, it checks for the existence of `window.parent`, `window.opener` and `window.parent.opener` (collectively called "Parents"). This function sends a "Handshake" message to each Parent via a `postMessage` in the hopes of discovering a DA. +:::note -If we run this logic in reverse we see that apps may be launched: +It is possible to have multiple DA Windows. For instance, a DA may propagate itself into new windows. Communication and coordination between DA Windows is an implementation detail and is not covered by this specification. -(1) By creating an iframe in a DA Window -(2) By calling `window.open` from a DA Window -(3) By creating an iframe in a window that was opened from a DA Window +::: -> Note - This covers most typical cases but it leaves out more complex launching scenarios such as "daisy chains" (frame -> frame or open -> open) or allowing end users to open tabs in an adhoc manner. To cover these cases, DAs may choose to allow themselves to be opened as hidden iframes within app windows. See the section on hidden iframes at the end of this document. +When an app runs `getAgent()`, it checks for the existence of `window.parent`, `window.opener` and `window.parent.opener` (and will continue up the chain of parent frames, e.g. `window.parent.parent`, `window.parent.parent.opener` until the reference to the next parent is equal to the current one (e.g. `window.parent.parent === window.parent` indicating that the frame does not have a parent). `getAgent()` will then send a standardized `WCP1Hello` message to each parent window or frame reference via [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) in to discover a DA. -## Responding to app instance connections - Web Connection Protocol (WCP) +Hence, apps may be launched: -DAs MUST run `addEventListener("message",...)` to receive incoming connection requests from apps. This will receive the initial `postMessage` "Handshake" messages from apps. Handshakes do not contain any identifying data. They contain only a "nonce" (a unique id). +1) By creating iframes in a DA Window +2) By calling `window.open` from a DA Window +3) By creating iframes in a window that was opened from a DA Window -Upon receiving an incoming Handshake the DA MUST: +and the Desktop Agent application will be found in a 'parent' of the application frame. -1) Create a [MessageChannel](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) -2) Set up a processing thread to receive and process BCP requests for this connection -3) Respond to the event's `source` window by echoing the nonce and transferring `port2` from the channel +## Responding to app instance connections - Web Connection Protocol (WCP) -Example WCP negotiation -```JavaScript -window.addEventListener("message", (event) => { - const { origin, data, source } = event - const channel = new MessageChannel() - startBCPProcessing(event, channel) // See next step - source.postMessage({ nonce: data.nonce }, origin, [channel.port2]) -}); -``` +Browser Resident DAs MUST call `window.addEventListener("message",...)` to receive incoming connection requests from apps, in the form of `WCP1Hello` messages defined in the [Web Connection Protocol](./webConnectionProtocol). -Alternatively for step 3, a DA MAY return a `url` instead of transferring a MessagePort. When `url` is provided in the response, `getAgent()` will open that `url` in a hidden iframe and initiate another WCP handshake with that frame. +Upon receiving an incoming `WCP1Hello` the DA MUST either: -Example WCP negotiation with URL -```JavaScript -window.addEventListener("message", (event) => { - const { origin, data, source } = event - source.postMessage({ nonce: data.nonce, url }, origin) -}); -``` +1) Respond with a `WCP2LoadURL` message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). + - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending `WCP1Hello` to the iframe. +2) Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. + - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a `WCPValidateAppIdentity` message from the application. + - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a `WCP3Handshake` message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel`` to the message. -## Responding to app communications with Desktop Agent Communication Protocol (DACP) +All further communication is conducted over the `MessageChannel`. The Desktop Agent should consider the newly created port to be inactive until a `WCPValidateAppIdentity` message is received via the MessagePort and successfully processed. -BCP processing should begin by setting up an inactive connection instance. This instance will become active after the first BCP "WCPValidateAppIdentity" message is received and processed (or deleted if it fails). It is important to remember the WindowProxy (event.source). +### Validating app identity -An `instanceUuid` should be established at this point. This will be used in following steps. +The first message received by the Desktop Agent on `MessagePort` `port1` from an application via a `MessageChannel` it created MUST be `WCPValidateAppIdentity`. Once received and successfully processed, the Desktop Agent MUST add handlers to it's `MessagePort` (`port1`) to process [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol.md) messages for this application and then respond with a `WCP5ValidateAppIdentityResponse` message containing an `appId`, `instanceId` and `instanceUuid` to identify the app and this specific instance. If validation fails, it should instead respond with `WCP5ValidateAppIdentityFailedResponse` and close the `MessageChannel`. -```JavaScript -const startBCPProcessing(event, channel) => { - const instanceUuid = uuid(); - const connection = { - status: "inactive", - windowProxy: event.source, - origin: event.origin, - channel, - instanceUuid - }; - connections[instanceUuid] = connection; - channel.port1.onmessage = (e) => { - processIncomingBCP(connection, e); // This function should switch based on message type - } -} -``` +App identity is validated and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent. This is achieved by matching `identityUrl`, `actualUrl` fields and `origin` of the `WCPValidateAppIdentity` message (supplied by the application via the `getAgent()` implementation) to the `details.url` field of the known App Directory records. -### Step 1 - Validating a connection +As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the `WCPValidateAppIdentity` message: +- `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. +- `identityUrl`: the URL to match to the app directory record. -The first BCP message received should be a "WCPValidateAppIdentity" message. With this message, the DA establishes or denies the connection. +The `actualUrl` field may be used for logging and debug purposes by the Desktop Agent. -1) If the message contains an `instanceUuid` field then the app window might have navigated or refreshed and simply require reconnecting on a previously established instance. The DA SHOULD reestablish the connection if the `instanceUuid`, `appId`, WindowProxy and origin match what is already on record. When a connection is reestablished the DA MUST respond with a valid "WCPValidateAppIdentityResponse" message. +Applications _may_ specify the `identityUrl` value are an argument to `getAgent()`. If not specified, the `getAgent()` implementation MUST use the current URL of the application. The Deksotp Agent must validate that the origin of the `identityUrl` is the same as the origin of _both_ the `actualUrl` and the `WCPValidateAppIdentity` message sent over the `MessagePort`. The Desktop Agent SHOULD then match the URL to that of applications known to the Desktop Agent. -2) If the message does not contain an `instanceUuid`, or the record does not match, then proceed to authenticate the application. +Owing to the fact that the different parts of a URL (origin, path, search parameters, anchor) are used differently by different web applications, matching of the `identityUrl` to known application URLs is more complex than a simple string match. To allow application developers control the requirements of matching, Desktop Agents MUST consider a URL to match if all elements (origin, path, search parameters, anchor) present in the App Directory record's URL are present in the `identityUrl`. As multiple App Directory may match a given identity URL, Desktop Agents SHOULD look for the best match that meets the requirements. -> Note - Apps that call `window.open()` to create new instances of themselves can appear to be their Parent. This is because browsers may clone SessionStorage for newly opened windows. When a child window calls `getAgent()` with the same appId as the parent window, it will appear to the DA that a navigation event occurred on the parent window (because `instanceUuid` will be set, and will appear to match the appId). DAs therefore MUST track the WindowProxy object that is used to establish each connection and use it as an additional comparison criteria. +For example, given an identity URL `url`, and an array of App Directory records `appDRecords`, a Desktop Agent SHOULD implement matching as follows: -Example BCP validation ```js -const processValidateAppIdentityBCP = (connection, e) => { - const { data, source, origin } = e; - - if(data.payload?.instanceUuid) { - const maybeExistingConnection = connections[data.payload.instanceUuid]; - // Maybe re-establish an existing connection that was lost due to page navigation - if (data.payload?.appId === maybeExistingConnection.appId && connection.windowProxy === maybeExistingConnection.windowProxy && origin === maybeExistingConnection.origin) { - // Close the ports that are now known to be defunct - maybeExistingConnection.channel.port1.close(); - maybeExistingConnection.channel.port2.close(); - - // Swap out the old and new connection - connections[maybeExistingConnection.instanceUuid] = connection; - delete connections[connection.instanceUuid]; - - // Copy the old into the new. The transfer is now complete. - connection.instanceUuid = maybeExistingConnection.instanceUuid; - connection.instanceId = maybeExistingConnection.instanceId; - connection.appId = maybeExistingConnection.appId; - connection.appMetadata = maybeExistingConnection.appMetadata; - connection.channel.port1.postMessage({ - type: "validateAppIdentityResponse", - payload: { - desktopAgentDetails: { - agentType: "PARENT", - appId: data.appId, - instanceUUid: data.instanceUuid, - instanceId: connection.instanceId, - }, - appMetaData: connection.appMetaData, - implementationMetadata // The DA's representation of itself as ImplementationMetadata type - } - }, connection.origin); - return; +/** Return the AppD record whose URL best matches the input URL or `null` if no match is found. + * To be considered a match all elements of the AppD URL (origin, path, searchParams and hash) + * must be found in the input URL. + * The best match is the AppD URL that contains the most elements from the + * input URL. + */ +let matchUrlToAppD = (url, appDRecords) => { + //parse the URL + const inputUrl = new URL(url); + + //fn to trim trailing / from paths + const trimTrailingSlash = (path) => { + if (path.endsWith("/")) { + const out = path.slice(0, -1); + //return null if it was just a / + return out === "" ? null : out; + } else { + return path; } - } - authenticateApp(connection, e); // See next step -} -``` - -### Step 2 - Authentication - -An app is authenticated by retrieving the app's AppD record and checking to ensure that the app's origin is contained in either the AppD record's `url` or `allowedOrigins` fields. Apps may provide either a fully qualified `appId` field (containing the domain of the AppD server) or a combination of `appId` and `appDUrl` fields (where `appDUrl` is the url of the AppD server). See [AppD endpoint specification](https://fdc3.finos.org/docs/app-directory/spec) - -**Examples** - -Each of these should result in a request to "https://myapp.com/appd/v2/apps/myApp" -`appId="myApp@myapp.com/appd` -`appId="myApp", appDUrl="https://muyapp.com/appd"` -`appDUrl="https://myapp.com/appd/v2/apps/myApp"` - -The DA MUST validate the app's identity against the application's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the _AppD record constructed from the WCPValidateAppIdentity message_. - -> Note - The AppD record provided by the app may be different from the one that the DA used to launch the app. This is because a DA may launch apps from its own app directory or even without a directory. However, the responsibility to provide application identity _rests with its publisher_ (the entity that is _hosting_ the app) and hence DA's MUST reference the AppD record that is provided by the application itself for verification. DAs MAY choose to implement _additional_ identity verification procedures (such as comparison with their own configured app directories) and MAY choose to deny access to an application for any reason. - -If authentication succeeds then the DA MUST respond with a "WCPValidateAppIdentityResponse" message which MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to support reconnecting after window navigation). - -```JavaScript -const authenticateApp = (connection, e) => { - const { data : {}, source, origin } = e; - if(source!==connection.windowProxy) { - // Message received from an unexpected window - return; - } - const { appId, appDUrl } = data; - const appDRecord = fetchAppDRecord(appId, appDUrl); // The DA implementor should implement AppD record fetching - if ( originMatch(origin, appDRecord)) { // The DA implementor should build origin matching logic - // The DA may implement any other validation here - // The DA may perform any internal record keeping here - connection.status = "active"; - connection.appId = appId; // Should be the resolved fully qualified appId - connection.instanceId = uuid(); - connection.appMetadata = new AppMetadata(); // To be set by the DA - connection.channel.port1.postMessage({ - type: "WCPValidateAppIdentityResponse", - payload: { - desktopAgentDetails: { - appId: data.appId, - instanceUUid: connection.instanceUuid, - instanceId: connection.instanceId, - }, - appMetaData: connection.appMetaData, - implementationMetadata // The DA's representation of itself as ImplementationMetadata type + }; + + const matchScores = []; + + //score appD records based on the match of their URL to the input URL + // if any component of the appD record URL is missing from input URL, score 0 + // otherwise count the number of input elements matched + appDRecords.map((record) => { + //record must contain a URL + if (!record.details?.url) { return; } + + //parse record URL + const parsedUrl = new URL(record.details.url); + + //origin of URL must match record + if (parsedUrl.origin !== inputUrl.origin) { return; } + let score = 1; + + //path must match if present in appD record - Path of "/" is ignored + const appDPath = trimTrailingSlash(parsedUrl.pathname); + if (appDPath) { + if (trimTrailingSlash(inputUrl.pathname) != appDPath) { + return; + } else { + score++; } - }, connection.origin) - } else { - connection.port.postMessage({ - type: "WCPValidateAppIdentityResponse", - payload: { - error: `Origin "${origin}" for "${appId}" didn't match AppD record` + } + + //hash must match if present in appD record + if (parsedUrl.hash) { + if (inputUrl.hash != parsedUrl.hash) { + return; + } else { + score++; } - }, connection.origin) - } -} + } + + if (!!parsedUrl.search) { + //search params present in appD record must be present in URL + const recordSearchKeys = parsedUrl.searchParams.keys().toArray(); + for (let i=0; i { + if (parsedUrl.searchParams.get(key) === inputUrl.searchParams.get(key)) { + score++; + } + }); + } + + //everything present in the record was found in the inputUrl + matchScores.push([score, record]); + }); + + //return the best scoring match (ignore ties) + matchScores.sort((a,b) => b[0]-a[0]); + return matchScores[0][1] ?? null; +}; ``` -### Step 3 - DesktopAgent Operations (BCP) +### Validating instance identity + +If the `WCPValidateAppIdentity` request message contains `instanceId` and `instanceUuid` fields then the window may represent an app instance that has navigated or refreshed and requires reconnecting with the previously assigned `instanceId`. The DA SHOULD reissue the same instanceId if the `instanceUuid`, `appId`, `WindowProxy` object and origin provided **all** match what is already on record and return details via the `WCP5ValidateAppIdentityResponse` message. + +If an `instanceId` and `instanceUuid` are not provided or do not pass the chjecks defined above, a new `instanceId` and `instanceUuid` should be generated by the Desktop Agent and returned in the `WCP5ValidateAppIdentityResponse` message. All generated `instanceId` and `instanceUuid` values should be retained by the DA (for the session or other appropriate period of time), along with a reference to the `WindowProxy` (`event.source` from the initial message received), as these can later be used for comparison to help determine if a new connection request is coming from a previously connected window which may or may not represent an existing app instance. If a previously connected window is reconnecting, any existing `MessageChannel` instance created for it can be cleaned up. + +Details of the received `instanceId` and `instanceUuid` are stored by the `getAgent()` implementation in [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) within the application window and are automatically reused when reconnecting to the Desktop Agent. SessionStorage is used as it is scoped to a particular window (rather than origin, as is the case with LocalStorage), allowing separate storage for each app instance. + +:::warn + +Apps launched via a call to `window.open()` from a window or app instance on the same origin can appear to be the original window. This is because browsers may clone SessionStorage for newly opened windows. When a child window calls `getAgent()` with the same `appId`, `instanceId` and `instanceUuid` as the parent window, it will appear to the DA that a navigation event occurred on the parent window. DAs therefore MUST also compare the `WindowProxy` object that is used to establish each connection to differentiate such cloned instances. If the WindowProxy objects do not match, then a new `instanceId` and `instanceUuid` MUST be assigned. + +::: + +For more details on the connection process, please see the documentation for the [Web Connection Protocol](./webConnectionProtocol). + +## Responding to app communications with Desktop Agent Communication Protocol (DACP) -Each message should be responded to with its corresponding response when a response should contain data, or with `BCPAck` if only an acknowledgement is required. See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) diff --git a/docs/api/specs/preloadDesktopAgents.md b/docs/api/specs/preloadDesktopAgents.md index 4fec204bd..0a40a8135 100644 --- a/docs/api/specs/preloadDesktopAgents.md +++ b/docs/api/specs/preloadDesktopAgents.md @@ -5,43 +5,24 @@ title: Preload Desktop Agents (next) --- -> Note - The [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) relies on Preload DAs behaving as specified in this document. FDC3 apps are now encouraged to use `getAgent()` instead of relying on the existence of the global object. +A Preload Desktop Agent is an FDC3 Desktop Agents (DA) supporting web applications that 'injects' or 'preloads' scripts into web windows which provide access to an FDC3 Desktop Agent API implementation. This document specifies the required behavior for a Preload Desktop Agent (DA). -## Injecting the global FDC3 object - -A Preload Desktop Agent MUST provide the FDC3 API via a global accessible as `window.fdc3`. Implementors MAY additionally make the API available through modules, imports, or other means. - -The global `window.fdc3` must only be available after the API is ready to use. To enable applications to avoid using the API before it is ready, implementors MUST provide a global `fdc3Ready` event that is fired when the API is ready for use. Implementations should first check for the existence of the FDC3 API and add a listener for this event if it is not found: +:::info -```js -function fdc3Action() { - // Make some fdc3 API calls here -} +> The [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) relies on Preload DAs behaving as specified in this document. -if (window.fdc3) { - fdc3Action(); -} else { - window.addEventListener('fdc3Ready', fdc3Action); -} -``` +::: -Since FDC3 is typically available to the whole web application, Desktop Agents are expected to make the [`DesktopAgent`](../api/ref/DesktopAgent) interface available at a global level. - -The global `window.fdc3` SHOULD only be available after the API is ready to use. To prevent the API from being used before it is ready, implementors SHOULD provide an `fdc3Ready` event. +## Injecting the global FDC3 object -## validateAppIdentity() +Since FDC3 is typically available to the whole web application, Desktop Agents are expected to make the [`DesktopAgent`](../api/ref/DesktopAgent) interface available at a global level. Hence, a Preload Desktop Agent MUST provide the FDC3 API via a global accessible as `window.fdc3`. Implementors MAY additionally make the API available through modules, imports, or other means. -In order to handle navigation events, Preload DAs SHOULD provide a `validateAppIdentity()` function on the global `fdc3` object (DesktopAgent interface). This function should behave the same as documented in the [Browser Resident DA Authentication Step](./browserResidentDesktopAgents.md#step-2---authentication), allowing the same combinations of appId and appDUrl as are allowed by `getAgent()`. +The global `window.fdc3` MUST only be available after the API is ready to use. Implementors MUST provide a global `fdc3Ready` event that is fired when the API is ready for use. -Example: +However, implementors SHOULD also ensure that the global is made available as soon as possible after the window is created to ensure that it can be detected by `getAgent()`. Queuing of requests to the API MAY be used to make the API available, while initialization is underway. -```js -const { instanceUuid, instanceId } = await fdc3.validateAppIdentity({ - appId: "yourApp@yourOrg.com", - instanceUuid: "some generated uuid" -}); -``` +:::note -The function should reject with an error string if the app identity does not match what is expected. +Prior to FDC3 2.2, apps were advised to check for the existence of the FDC3 API at `window.fdc3` and then add a listener for the `fdc3Ready` event if it was not found. This has since been superseded by the recommendation to use `getAgent()`, which handles those steps internally, alongside supporting FDC3 in web browsers ( via the [Browser Resident Desktop Agent spec](./browserResidentDesktopAgents)). -If a Preload DA does not provide the validateAppIdentity() function this it assumes responsibility for handling navigation and refresh events. +::: From 3543edd82a8730800984aac7783345dfd01c760a Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 3 Sep 2024 11:08:13 +0100 Subject: [PATCH 071/152] Fixing HeartbeatAcknowledgementRequest type --- schemas/api/appRequest.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schemas/api/appRequest.schema.json b/schemas/api/appRequest.schema.json index cb4a25229..00f521c78 100644 --- a/schemas/api/appRequest.schema.json +++ b/schemas/api/appRequest.schema.json @@ -25,7 +25,7 @@ "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", - "heartbeatResponseRequest", + "heartbeatAcknowledgementRequest", "intentListenerUnsubscribeRequest", "intentResultRequest", "joinUserChannelRequest", From acc03b9a959376f52d28db13d7c89957c93b0308 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 3 Sep 2024 11:08:25 +0100 Subject: [PATCH 072/152] Adding actualUrl to WCP messages --- .../api/specs/browserResidentDesktopAgents.md | 2 +- schemas/api/WCP1Hello.schema.json | 8 +- .../api/WCP4ValidateAppIdentity.schema.json | 8 +- src/api/BrowserTypes.ts | 116 ++++++++++-------- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 2a6655dcc..9dff84c1b 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -193,7 +193,7 @@ For more details on the connection process, please see the documentation for the See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) -See bcp.ts for a full list of BCP messages. +See bcp.ts for a full list of BCP messages. ## Implementing DAs in hidden iframes diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 83ce743ce..04db3d4cd 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -30,6 +30,12 @@ "type": "string", "format": "uri" }, + "actualUrl": { + "title": "Actual URL", + "description": "The current URL of the page attempting to connect. This may differ from the identityUrl, but the origins MUST match.", + "type": "string", + "format": "uri" + }, "fdc3Version": { "title": "FDC3 version", "description": "The version of FDC3 API that the app supports.", @@ -46,7 +52,7 @@ "type": "boolean" } }, - "required": ["identityUrl","fdc3Version"] + "required": ["identityUrl","actualUrl","fdc3Version"] }, "meta": true }, diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json index 6ab04e85a..9ef23a20a 100644 --- a/schemas/api/WCP4ValidateAppIdentity.schema.json +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -30,6 +30,12 @@ "type": "string", "format": "uri" }, + "actualUrl": { + "title": "Actual URL", + "description": "The current URL of the page attempting to connect. This may differ from the identityUrl, but the origins MUST match.", + "type": "string", + "format": "uri" + }, "instanceId": { "title": "instanceId", "description": "If an application has previously connected to the desktop agent, it may specify its prior instance id and associated instance UUID to request the same same instance Id be assigned.", @@ -43,7 +49,7 @@ }, "additionalProperties": false, "required": [ - "identityUrl" + "identityUrl", "actualUrl" ] }, "meta": true diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index b2fd1568d..e75decf5c 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatEvent, HeartbeatResponseRequest, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatAcknowledgementRequest, HeartbeatEvent, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -40,8 +40,8 @@ // const getOrCreateChannelResponse = Convert.toGetOrCreateChannelResponse(json); // const getUserChannelsRequest = Convert.toGetUserChannelsRequest(json); // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); +// const heartbeatAcknowledgementRequest = Convert.toHeartbeatAcknowledgementRequest(json); // const heartbeatEvent = Convert.toHeartbeatEvent(json); -// const heartbeatResponseRequest = Convert.toHeartbeatResponseRequest(json); // const iframeChannels = Convert.toIframeChannels(json); // const iframeChannelSelected = Convert.toIframeChannelSelected(json); // const iframeDrag = Convert.toIframeDrag(json); @@ -618,7 +618,7 @@ export interface AppRequestMessageMeta { * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "eventListenerUnsubscribeRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "heartbeatResponseRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; +export type RequestMessageType = "addContextListenerRequest" | "addEventListenerRequest" | "addIntentListenerRequest" | "broadcastRequest" | "contextListenerUnsubscribeRequest" | "createPrivateChannelRequest" | "eventListenerUnsubscribeRequest" | "findInstancesRequest" | "findIntentRequest" | "findIntentsByContextRequest" | "getAppMetadataRequest" | "getCurrentChannelRequest" | "getCurrentContextRequest" | "getInfoRequest" | "getOrCreateChannelRequest" | "getUserChannelsRequest" | "heartbeatAcknowledgementRequest" | "intentListenerUnsubscribeRequest" | "intentResultRequest" | "joinUserChannelRequest" | "leaveCurrentChannelRequest" | "openRequest" | "privateChannelAddEventListenerRequest" | "privateChannelDisconnectRequest" | "privateChannelUnsubscribeEventListenerRequest" | "raiseIntentForContextRequest" | "raiseIntentRequest"; /** * An event message from the Desktop Agent to an app indicating that context has been @@ -2017,78 +2017,78 @@ export interface GetUserChannelsResponsePayload { */ /** - * A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is - * alive and that the application should send a heartbeatResponseRequest to the agent in - * response. + * A request that serves as an acknowledgement of a heartbeat event from the Desktop Agent + * and indicates that an application window or frame is still alive. * - * A message from a Desktop Agent to an FDC3-enabled app representing an event. + * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface HeartbeatEvent { +export interface HeartbeatAcknowledgementRequest { /** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ - meta: AddEventListenerEventMeta; + meta: AddContextListenerRequestMeta; /** - * The message payload contains details of the event that the app is being notified about. + * The message payload typically contains the arguments to FDC3 API functions. */ - payload: HeartbeatEventPayload; + payload: HeartbeatAcknowledgementRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "heartbeatEvent"; + type: "heartbeatAcknowledgementRequest"; } /** - * The message payload contains details of the event that the app is being notified about. + * The message payload typically contains the arguments to FDC3 API functions. */ -export interface HeartbeatEventPayload { +export interface HeartbeatAcknowledgementRequestPayload { /** - * The time at which the heartbeat event was sent, which should be quoted in the response + * The timestamp of the heartbeatEvent that is being acknowledged. */ timestamp: Date; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ /** - * A request that serves as a response to a heartbeat event from the Desktop Agent and - * indicates that an application window or frame is still alive. + * A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is + * alive and that the application should send a heartbeatResponseRequest to the agent in + * response. * - * A request message from an FDC3-enabled app to a Desktop Agent. + * A message from a Desktop Agent to an FDC3-enabled app representing an event. */ -export interface HeartbeatResponseRequest { +export interface HeartbeatEvent { /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddContextListenerRequestMeta; + meta: AddEventListenerEventMeta; /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload contains details of the event that the app is being notified about. */ - payload: HeartbeatResponseRequestPayload; + payload: HeartbeatEventPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "heartbeatResponseRequest"; + type: "heartbeatEvent"; } /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload contains details of the event that the app is being notified about. */ -export interface HeartbeatResponseRequestPayload { +export interface HeartbeatEventPayload { /** - * The timestamp of the heartbeatEvent that is being responded to. + * The time at which the heartbeat event was sent, which should be quoted in the response */ timestamp: Date; } /** * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ /** @@ -3579,6 +3579,11 @@ export interface ConnectionStepMetadata { * The message payload, containing data pertaining to this connection step. */ export interface WebConnectionProtocol1HelloPayload { + /** + * The current URL of the page attempting to connect. This may differ from the identityUrl, + * but the origins MUST match. + */ + actualUrl: string; /** * A flag that may be used to indicate that a channel selector UI is or is not required. If * the app includes its own UI for displaying @@ -3709,6 +3714,11 @@ export interface WebConnectionProtocol4ValidateAppIdentity { * The message payload, containing data pertaining to this connection step. */ export interface WebConnectionProtocol4ValidateAppIdentityPayload { + /** + * The current URL of the page attempting to connect. This may differ from the identityUrl, + * but the origins MUST match. + */ + actualUrl: string; /** * URL to use for the identity of the application. Desktop Agents MUST validate that the * origin of the message matches the URL, but MAY implement custom comparison logic. @@ -4157,20 +4167,20 @@ export class Convert { return JSON.stringify(uncast(value, r("GetUserChannelsResponse")), null, 2); } - public static toHeartbeatEvent(json: string): HeartbeatEvent { - return cast(JSON.parse(json), r("HeartbeatEvent")); + public static toHeartbeatAcknowledgementRequest(json: string): HeartbeatAcknowledgementRequest { + return cast(JSON.parse(json), r("HeartbeatAcknowledgementRequest")); } - public static heartbeatEventToJson(value: HeartbeatEvent): string { - return JSON.stringify(uncast(value, r("HeartbeatEvent")), null, 2); + public static heartbeatAcknowledgementRequestToJson(value: HeartbeatAcknowledgementRequest): string { + return JSON.stringify(uncast(value, r("HeartbeatAcknowledgementRequest")), null, 2); } - public static toHeartbeatResponseRequest(json: string): HeartbeatResponseRequest { - return cast(JSON.parse(json), r("HeartbeatResponseRequest")); + public static toHeartbeatEvent(json: string): HeartbeatEvent { + return cast(JSON.parse(json), r("HeartbeatEvent")); } - public static heartbeatResponseRequestToJson(value: HeartbeatResponseRequest): string { - return JSON.stringify(uncast(value, r("HeartbeatResponseRequest")), null, 2); + public static heartbeatEventToJson(value: HeartbeatEvent): string { + return JSON.stringify(uncast(value, r("HeartbeatEvent")), null, 2); } public static toIframeChannels(json: string): IframeChannels { @@ -5071,6 +5081,14 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "userChannels", js: "userChannels", typ: u(undefined, a(r("Channel"))) }, ], false), + "HeartbeatAcknowledgementRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("HeartbeatAcknowledgementRequestPayload") }, + { json: "type", js: "type", typ: r("HeartbeatAcknowledgementRequestType") }, + ], false), + "HeartbeatAcknowledgementRequestPayload": o([ + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), "HeartbeatEvent": o([ { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, { json: "payload", js: "payload", typ: r("HeartbeatEventPayload") }, @@ -5079,14 +5097,6 @@ const typeMap: any = { "HeartbeatEventPayload": o([ { json: "timestamp", js: "timestamp", typ: Date }, ], false), - "HeartbeatResponseRequest": o([ - { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, - { json: "payload", js: "payload", typ: r("HeartbeatResponseRequestPayload") }, - { json: "type", js: "type", typ: r("HeartbeatResponseRequestType") }, - ], false), - "HeartbeatResponseRequestPayload": o([ - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), "IframeChannels": o([ { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, { json: "type", js: "type", typ: r("IframeChannelsType") }, @@ -5407,6 +5417,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "WebConnectionProtocol1HelloPayload": o([ + { json: "actualUrl", js: "actualUrl", typ: "" }, { json: "channelSelector", js: "channelSelector", typ: u(undefined, true) }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, { json: "identityUrl", js: "identityUrl", typ: "" }, @@ -5436,6 +5447,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("WebConnectionProtocol4ValidateAppIdentityType") }, ], false), "WebConnectionProtocol4ValidateAppIdentityPayload": o([ + { json: "actualUrl", js: "actualUrl", typ: "" }, { json: "identityUrl", js: "identityUrl", typ: "" }, { json: "instanceId", js: "instanceId", typ: u(undefined, "") }, { json: "instanceUuid", js: "instanceUuid", typ: u(undefined, "") }, @@ -5589,7 +5601,7 @@ const typeMap: any = { "getInfoRequest", "getOrCreateChannelRequest", "getUserChannelsRequest", - "heartbeatResponseRequest", + "heartbeatAcknowledgementRequest", "intentListenerUnsubscribeRequest", "intentResultRequest", "joinUserChannelRequest", @@ -5705,12 +5717,12 @@ const typeMap: any = { "GetUserChannelsResponseType": [ "getUserChannelsResponse", ], + "HeartbeatAcknowledgementRequestType": [ + "heartbeatAcknowledgementRequest", + ], "HeartbeatEventType": [ "heartbeatEvent", ], - "HeartbeatResponseRequestType": [ - "heartbeatResponseRequest", - ], "IframeChannelsType": [ "iframeChannels", ], From 673875866b39bf3d503c5ec2729fa00a26f6088c Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 3 Sep 2024 11:35:41 +0100 Subject: [PATCH 073/152] Update validateIdentity function --- src/api/DesktopAgent.ts | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts index c69298654..8dcbe1461 100644 --- a/src/api/DesktopAgent.ts +++ b/src/api/DesktopAgent.ts @@ -549,22 +549,31 @@ export interface DesktopAgent { getAppMetadata(app: AppIdentifier): Promise; /** - * Use by getAgent() to validate app identity. Apps should not call this function directly. - * - * See getAgent.md for instructions. - * - * Either appId, appDUrl, or both may be provided. This logic is covered int supported_platforms.md. + * @experimental + * Optional function, that can be used by getAgent() to validate app identity + * in preload Desktop Agents. Apps should not call this function directly. + * + * See `getAgent` for instructions. + * + * `identityUrl` is used to identify an application and may be used to match + * to an appD record known to the Desktop Agent. As applications may navigate + * to different URLs (changing their path, search parameters or hash) during + * normal use they may provide a URL to use to identify themselves. Preload + * Desktop Agents can usually determine the current URL of the window and + * MUST validate that it matches the origin of the `identityUrl`. + * + * Instance identity should be determined by a preload Desktop agent + * internally, without the need for an instanceUuid as used with Web Desktop Agents. * + * In the event that instance identity cannot be validated, the Desktop Agent + * MUST prevent further communication with the application. + * * This function is optional but recommended. */ validateAppIdentity?({ - appId, - appDUrl, - instanceUuid, + identityUrl }: { - appId?: string; - appDUrl?: string; - instanceUuid?: string; + identityUrl: string; }): Promise; //--------------------------------------------------------------------------------------------- From c5465351ef18a005afdae002dc5113acd731ce2a Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 4 Sep 2024 10:53:20 +0100 Subject: [PATCH 074/152] remove `validateIdentity()` --- docs/api/spec.md | 1 - .../api/specs/browserResidentDesktopAgents.md | 3 +- docs/api/specs/preloadDesktopAgents.md | 2 +- docs/api/specs/webConnectionProtocol.md | 10 ++----- src/api/DesktopAgent.ts | 28 ------------------- 5 files changed, 5 insertions(+), 39 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 4159ebdae..f5194a13b 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -131,7 +131,6 @@ An FDC3 Standard compliant Desktop Agent implementation **SHOULD**: - Make metadata about each context message or intent and context message received (including the app that originated the message) available to the receiving application. - Prevent external apps from listening or publishing on a [`PrivateChannel`](ref/PrivateChannel) that they did not request or provide. - Enforce compliance with the expected behavior of intents (where Intents specify a contract that is enforceable by schema, for example, return object types) and return an error if the interface is not met. -- Implement [`validateAppIdentity()`](ref/DesktopAgent#validateappidentity). An FDC3 Standard compliant Desktop Agent implementation **MAY**: diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 9dff84c1b..b4e2c4901 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -76,7 +76,8 @@ The first message received by the Desktop Agent on `MessagePort` `port1` from an App identity is validated and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent. This is achieved by matching `identityUrl`, `actualUrl` fields and `origin` of the `WCPValidateAppIdentity` message (supplied by the application via the `getAgent()` implementation) to the `details.url` field of the known App Directory records. -As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the `WCPValidateAppIdentity` message: +As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the `WCPValidateAppIdentity` message: + - `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. - `identityUrl`: the URL to match to the app directory record. diff --git a/docs/api/specs/preloadDesktopAgents.md b/docs/api/specs/preloadDesktopAgents.md index 0a40a8135..5686ea358 100644 --- a/docs/api/specs/preloadDesktopAgents.md +++ b/docs/api/specs/preloadDesktopAgents.md @@ -9,7 +9,7 @@ A Preload Desktop Agent is an FDC3 Desktop Agents (DA) supporting web applicatio :::info -> The [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) relies on Preload DAs behaving as specified in this document. +> The [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) relies on Preload DAs behaving as specified in this document. ::: diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 84558535a..05eb82cd3 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -107,15 +107,9 @@ Apps must identify themselves so that DAs can positively associate them with the The DA will validate the app's identity against the app window's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the AppD record referenced by either appId or appDUrl. To ensure the validity of this process, appD records and the application itself SHOULD be served via HTTPS. -1) If the DA is injected (using the `fdc` global object - Preload DA) then: +1) If the DA is injected (using the `fdc3` global object - Preload DA) then: - Await the function `fdc3.validateAppIdentity()` if that function exists. `instanceUuid` SHOULD be provided if available from SessionStorage. - - The DA SHOULD validate the app identity against the URL of the window (window.location.href). If successful then it will resolve to an `AppMetaData` object, otherwise it will reject with `IdentityValidationFailed` error. - - After rejection, the DA will not respond to any further FDC3 API calls from the application instance. - - If the `fdc3.validateAppIdentity` function does not exist then simply return the global object as the DesktopAgent. (This should only occur when using legacy Desktop Agents.) + Simply return the global object as the DesktopAgent. 2) If the DA is browser-resident, then: diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts index 8dcbe1461..d69c9e24d 100644 --- a/src/api/DesktopAgent.ts +++ b/src/api/DesktopAgent.ts @@ -548,34 +548,6 @@ export interface DesktopAgent { */ getAppMetadata(app: AppIdentifier): Promise; - /** - * @experimental - * Optional function, that can be used by getAgent() to validate app identity - * in preload Desktop Agents. Apps should not call this function directly. - * - * See `getAgent` for instructions. - * - * `identityUrl` is used to identify an application and may be used to match - * to an appD record known to the Desktop Agent. As applications may navigate - * to different URLs (changing their path, search parameters or hash) during - * normal use they may provide a URL to use to identify themselves. Preload - * Desktop Agents can usually determine the current URL of the window and - * MUST validate that it matches the origin of the `identityUrl`. - * - * Instance identity should be determined by a preload Desktop agent - * internally, without the need for an instanceUuid as used with Web Desktop Agents. - * - * In the event that instance identity cannot be validated, the Desktop Agent - * MUST prevent further communication with the application. - * - * This function is optional but recommended. - */ - validateAppIdentity?({ - identityUrl - }: { - identityUrl: string; - }): Promise; - //--------------------------------------------------------------------------------------------- // Deprecated function signatures //--------------------------------------------------------------------------------------------- From ed60bfc044f30593f3f17cd9c5a70ca6f8382f14 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 4 Sep 2024 11:13:42 +0100 Subject: [PATCH 075/152] adding eventUuid reference to intentResultRequest + minor tidy up around intent results --- schemas/api/agentEvent.schema.json | 2 +- schemas/api/api.schema.json | 37 ++++++++++++++++ schemas/api/intentResultRequest.schema.json | 43 +++---------------- .../api/raiseIntentResultResponse.schema.json | 38 +--------------- src/api/BrowserTypes.ts | 33 +++++++------- 5 files changed, 64 insertions(+), 89 deletions(-) diff --git a/schemas/api/agentEvent.schema.json b/schemas/api/agentEvent.schema.json index 58b16dccc..80c358b6d 100644 --- a/schemas/api/agentEvent.schema.json +++ b/schemas/api/agentEvent.schema.json @@ -35,7 +35,7 @@ "$ref": "common.schema.json#/$defs/Timestamp" }, "eventUuid": { - "$ref": "common.schema.json#/$defs/ResponseUuid" + "$ref": "common.schema.json#/$defs/EventUuid" } }, "required": ["timestamp", "eventUuid"], diff --git a/schemas/api/api.schema.json b/schemas/api/api.schema.json index def36e745..c9ffbb568 100644 --- a/schemas/api/api.schema.json +++ b/schemas/api/api.schema.json @@ -468,6 +468,43 @@ "source" ] }, + "IntentResult": { + "title": "IntentResult", + "anyOf": [ + { + "type": "object", + "title": "IntentResult Context", + "properties": { + "context": { + "$ref": "../context/context.schema.json" + } + }, + "required": [ + "context" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Channel", + "properties": { + "channel": { + "$ref": "#/definitions/Channel" + } + }, + "required": [ + "channel" + ], + "additionalProperties": false + }, + { + "type": "object", + "title": "IntentResult Void", + "properties": {}, + "additionalProperties": false + } + ] + }, "FDC3EventType": { "title": "FDC3 Event Type", "description": "The type of a (non-context and non-intent) event that may be received via the FDC3 API's addEventListener function.", diff --git a/schemas/api/intentResultRequest.schema.json b/schemas/api/intentResultRequest.schema.json index 4f0826eae..c83e6004e 100644 --- a/schemas/api/intentResultRequest.schema.json +++ b/schemas/api/intentResultRequest.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/IntentResultRequest.schema.json", "type": "object", "title": "IntentResult Request", - "description": "A request to deliver a result for an intent (which may include a `void` result that just indicates that the handler has run, returning no result).", + "description": "A request to deliver a result for an intent (which may include a `void` result that just indicates that the handler has run, returning no result). The result is tied to the intentEvent it relates to by quoting the `eventUuid` of the intentEvent in its payload.", "allOf": [ { "$ref": "appRequest.schema.json" @@ -31,42 +31,13 @@ "title": "IntentResult Request Payload", "type": "object", "properties": { + "intentEventUuid": { + "title": "IntentEvent UUID", + "type": "string", + "description": "The eventUuid value of the intentEvent that the result being sent relates to." + }, "intentResult": { - "title": "IntentResult", - "anyOf": [ - { - "type": "object", - "title": "IntentResult Context", - "properties": { - "context": { - "$ref": "../context/context.schema.json" - } - }, - "required": [ - "context" - ], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Channel", - "properties": { - "channel": { - "$ref": "api.schema.json#/definitions/Channel" - } - }, - "required": [ - "channel" - ], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Void", - "properties": {}, - "additionalProperties": false - } - ] + "$ref": "api.schema.json#/definitions/IntentResult" } }, "required": [ diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index 9bb7b6925..f4f1419f3 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -3,7 +3,7 @@ "$id": "https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json", "type": "object", "title": "RaiseIntentResult Response", - "description": "A secondary response to a request to raise an intent used to deliver the intent result.", + "description": "A secondary response to a request to raise an intent used to deliver the intent result. This message should quote the original requestUuid of the raiseIntentRequest message in its `meta.requestUuid` field.", "allOf": [ { "$ref": "agentResponse.schema.json" @@ -39,41 +39,7 @@ "type": "object", "properties": { "intentResult": { - "title": "IntentResult", - "anyOf": [ - { - "type": "object", - "title": "IntentResult Context", - "properties": { - "context": { - "$ref": "../context/context.schema.json" - } - }, - "required": [ - "context" - ], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Channel", - "properties": { - "channel": { - "$ref": "api.schema.json#/definitions/Channel" - } - }, - "required": [ - "channel" - ], - "additionalProperties": false - }, - { - "type": "object", - "title": "IntentResult Void", - "properties": {}, - "additionalProperties": false - } - ] + "$ref": "api.schema.json#/definitions/IntentResult" } }, "required": [ diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index e75decf5c..096482950 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1317,6 +1317,8 @@ export interface Image { * response to addContextListener, addIntentListener or one of the PrivateChannel event * listeners and used to identify it in messages (e.g. when unsubscribing). * + * Unique identifier for an event message sent from a Desktop Agent to an app. + * * Unique identifier for a for an attempt to connect to a Desktop Agent * * Should be set if the raiseIntent request returned an error. @@ -2620,7 +2622,8 @@ export interface IntentListenerUnsubscribeResponse { /** * A request to deliver a result for an intent (which may include a `void` result that just - * indicates that the handler has run, returning no result). + * indicates that the handler has run, returning no result). The result is tied to the + * intentEvent it relates to by quoting the `eventUuid` of the intentEvent in its payload. * * A request message from an FDC3-enabled app to a Desktop Agent. */ @@ -2644,10 +2647,14 @@ export interface IntentResultRequest { * The message payload typically contains the arguments to FDC3 API functions. */ export interface IntentResultRequestPayload { - intentResult: PurpleIntentResult; + /** + * The eventUuid value of the intentEvent that the result being sent relates to. + */ + intentEventUuid?: string; + intentResult: IntentResult; } -export interface PurpleIntentResult { +export interface IntentResult { context?: Context; channel?: Channel; } @@ -3506,6 +3513,8 @@ export interface RaiseIntentResponsePayload { /** * A secondary response to a request to raise an intent used to deliver the intent result. + * This message should quote the original requestUuid of the raiseIntentRequest message in + * its `meta.requestUuid` field. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. @@ -3535,12 +3544,7 @@ export interface RaiseIntentResultResponse { */ export interface RaiseIntentResultResponsePayload { error?: ResponsePayloadError; - intentResult?: FluffyIntentResult; -} - -export interface FluffyIntentResult { - context?: Context; - channel?: Channel; + intentResult?: IntentResult; } /** @@ -5218,9 +5222,10 @@ const typeMap: any = { { json: "type", js: "type", typ: r("IntentResultRequestType") }, ], false), "IntentResultRequestPayload": o([ - { json: "intentResult", js: "intentResult", typ: r("PurpleIntentResult") }, + { json: "intentEventUuid", js: "intentEventUuid", typ: u(undefined, "") }, + { json: "intentResult", js: "intentResult", typ: r("IntentResult") }, ], false), - "PurpleIntentResult": o([ + "IntentResult": o([ { json: "context", js: "context", typ: u(undefined, r("Context")) }, { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, ], false), @@ -5401,11 +5406,7 @@ const typeMap: any = { ], false), "RaiseIntentResultResponsePayload": o([ { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, - { json: "intentResult", js: "intentResult", typ: u(undefined, r("FluffyIntentResult")) }, - ], false), - "FluffyIntentResult": o([ - { json: "context", js: "context", typ: u(undefined, r("Context")) }, - { json: "channel", js: "channel", typ: u(undefined, r("Channel")) }, + { json: "intentResult", js: "intentResult", typ: u(undefined, r("IntentResult")) }, ], false), "WebConnectionProtocol1Hello": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, From abcc5776f4908ac343f170990e19789d7410736b Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 4 Sep 2024 11:35:57 +0100 Subject: [PATCH 076/152] Adjusting iframe messages --- schemas/api/iFrameHello.schema.json | 9 ++-- schemas/api/iFrameRestyle.schema.json | 24 ++-------- src/api/BrowserTypes.ts | 67 ++++++++++----------------- 3 files changed, 35 insertions(+), 65 deletions(-) diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index b584c7c6f..d3658f00b 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -32,19 +32,20 @@ "initialCSS": { "title": "Initial CSS", "type": "object", - "description": "A constrained set of CSS properties that should be set on the iframe before it is displayed.", + "description": "A constrained set of CSS properties that should be set on the iframe before it is displayed. Note `position` cannot be specified and should always be set to `fixed`.", "properties": { "height": {"type": "string", "title": "height", "description": "The initial height of the iframe"}, "width": {"type": "string", "title": "width", "description": "The initial width of the iframe"}, - "position": {"type": "string", "title": "position", "description": "The initial position CSS to apply to the iframe"}, "zIndex": {"type": "string", "title": "zIndex", "description": "The initial zindex to apply to the iframe"}, "left": {"type": "string", "title": "left", "description": "The initial left property to apply to the iframe"}, - "top": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, + "top": {"type": "string", "title": "top", "description": "The initial top property to apply to the iframe"}, + "bottom": {"type": "string", "title": "left", "description": "The initial bottom property to apply to the iframe"}, + "right": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, "transition": {"type": "string", "title": "transition", "description": "The transition property to apply to the iframe"}, "maxHeight": {"type": "string", "title": "maxHeight", "description": "The maximum height to apply to the iframe"}, "maxWidth": {"type": "string", "title": "maxWidth", "description": "The maximum with to apply to the iframe"} }, - "required": ["height", "width", "position", "top", "left"] + "required": ["height", "width", "top", "left"] } }, "additionalProperties": false, diff --git a/schemas/api/iFrameRestyle.schema.json b/schemas/api/iFrameRestyle.schema.json index 9ed06f423..773d6b981 100644 --- a/schemas/api/iFrameRestyle.schema.json +++ b/schemas/api/iFrameRestyle.schema.json @@ -27,34 +27,20 @@ "updatedCSS": { "title": "Updated CSS", "type": "object", - "description": "A constrained set of CSS properties that should be applied to the iframe.", + "description": "A constrained set of CSS properties that should be applied to the iframe. Note `position` cannot be set, and should always be `fixed`.", "properties": { "height": {"type": "string", "title": "height", "description": "The updated height of the iframe"}, "width": {"type": "string", "title": "width", "description": "The updated width of the iframe"}, - "position": {"type": "string", "title": "position", "description": "The updated position CSS to apply to the iframe"}, "zIndex": {"type": "string", "title": "zIndex", "description": "The updated zindex to apply to the iframe"}, - "left": {"type": "string", "title": "left", "description": "The updated left property to apply to the iframe"}, - "top": {"type": "string", "title": "top", "description": "The updated right property to apply to the iframe"}, + "left": {"type": "string", "title": "left", "description": "The initial left property to apply to the iframe"}, + "top": {"type": "string", "title": "top", "description": "The initial top property to apply to the iframe"}, + "bottom": {"type": "string", "title": "left", "description": "The initial bottom property to apply to the iframe"}, + "right": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, "transition": {"type": "string", "title": "transition", "description": "The updated transition property to apply to the iframe"}, "maxHeight": {"type": "string", "title": "maxHeight", "description": "The updated maximum height to apply to the iframe"}, "maxWidth": {"type": "string", "title": "maxWidth", "description": "The updated maximum with to apply to the iframe"} }, "required": [] - }, - "resizeAnchor": { - "title": "Resizing", - "description": "Optional property to support resizing. When resizing anchor at the indicated location (without top or left set),\ne.g.\n\t- for top-left and a larger size: the bottom right corner should move down and out.\n\t- for top and smaller size: both the bottom corners should move in and up.", - "anyOf": [ - { "type": "string", "const": "top-left" }, - { "type": "string", "const": "top" }, - { "type": "string", "const": "top-right" }, - { "type": "string", "const": "right" }, - { "type": "string", "const": "bottom-right" }, - { "type": "string", "const": "bottom" }, - { "type": "string", "const": "bottom-left" }, - { "type": "string", "const": "left" }, - { "type": "string", "const": "center" } - ] } }, "additionalProperties": false, diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 096482950..72d2e62d8 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -2271,16 +2271,20 @@ export interface IframeHelloPayload { implementationDetails: string; /** * A constrained set of CSS properties that should be set on the iframe before it is - * displayed. + * displayed. Note `position` cannot be specified and should always be set to `fixed`. */ initialCSS: InitialCSS; } /** * A constrained set of CSS properties that should be set on the iframe before it is - * displayed. + * displayed. Note `position` cannot be specified and should always be set to `fixed`. */ export interface InitialCSS { + /** + * The initial bottom property to apply to the iframe + */ + bottom?: string; /** * The initial height of the iframe */ @@ -2298,11 +2302,11 @@ export interface InitialCSS { */ maxWidth?: string; /** - * The initial position CSS to apply to the iframe + * The initial right property to apply to the iframe */ - position: string; + right?: string; /** - * The initial right property to apply to the iframe + * The initial top property to apply to the iframe */ top: string; /** @@ -2444,38 +2448,27 @@ export interface IframeRestyle { */ export interface IframeRestylePayload { /** - * Optional property to support resizing. When resizing anchor at the indicated location - * (without top or left set), - * e.g. - * - for top-left and a larger size: the bottom right corner should move down and out. - * - for top and smaller size: both the bottom corners should move in and up. - */ - resizeAnchor?: Resizing; - /** - * A constrained set of CSS properties that should be applied to the iframe. + * A constrained set of CSS properties that should be applied to the iframe. Note `position` + * cannot be set, and should always be `fixed`. */ updatedCSS: UpdatedCSS; } /** - * Optional property to support resizing. When resizing anchor at the indicated location - * (without top or left set), - * e.g. - * - for top-left and a larger size: the bottom right corner should move down and out. - * - for top and smaller size: both the bottom corners should move in and up. - */ -export type Resizing = "top-left" | "top" | "top-right" | "right" | "bottom-right" | "bottom" | "bottom-left" | "left" | "center"; - -/** - * A constrained set of CSS properties that should be applied to the iframe. + * A constrained set of CSS properties that should be applied to the iframe. Note `position` + * cannot be set, and should always be `fixed`. */ export interface UpdatedCSS { + /** + * The initial bottom property to apply to the iframe + */ + bottom?: string; /** * The updated height of the iframe */ height?: string; /** - * The updated left property to apply to the iframe + * The initial left property to apply to the iframe */ left?: string; /** @@ -2487,11 +2480,11 @@ export interface UpdatedCSS { */ maxWidth?: string; /** - * The updated position CSS to apply to the iframe + * The initial right property to apply to the iframe */ - position?: string; + right?: string; /** - * The updated right property to apply to the iframe + * The initial top property to apply to the iframe */ top?: string; /** @@ -5143,11 +5136,12 @@ const typeMap: any = { { json: "initialCSS", js: "initialCSS", typ: r("InitialCSS") }, ], false), "InitialCSS": o([ + { json: "bottom", js: "bottom", typ: u(undefined, "") }, { json: "height", js: "height", typ: "" }, { json: "left", js: "left", typ: "" }, { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, - { json: "position", js: "position", typ: "" }, + { json: "right", js: "right", typ: u(undefined, "") }, { json: "top", js: "top", typ: "" }, { json: "transition", js: "transition", typ: u(undefined, "") }, { json: "width", js: "width", typ: "" }, @@ -5179,15 +5173,15 @@ const typeMap: any = { { json: "type", js: "type", typ: r("IframeRestyleType") }, ], false), "IframeRestylePayload": o([ - { json: "resizeAnchor", js: "resizeAnchor", typ: u(undefined, r("Resizing")) }, { json: "updatedCSS", js: "updatedCSS", typ: r("UpdatedCSS") }, ], false), "UpdatedCSS": o([ + { json: "bottom", js: "bottom", typ: u(undefined, "") }, { json: "height", js: "height", typ: u(undefined, "") }, { json: "left", js: "left", typ: u(undefined, "") }, { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, - { json: "position", js: "position", typ: u(undefined, "") }, + { json: "right", js: "right", typ: u(undefined, "") }, { json: "top", js: "top", typ: u(undefined, "") }, { json: "transition", js: "transition", typ: u(undefined, "") }, { json: "width", js: "width", typ: u(undefined, "") }, @@ -5760,17 +5754,6 @@ const typeMap: any = { "IframeResolveActionType": [ "iframeResolveAction", ], - "Resizing": [ - "bottom", - "bottom-left", - "bottom-right", - "center", - "left", - "right", - "top", - "top-left", - "top-right", - ], "IframeRestyleType": [ "iframeRestyle", ], From 065c0d78ba9ff011eb9c4616a8695890b446bbe8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 4 Sep 2024 17:26:21 +0100 Subject: [PATCH 077/152] Adjusting module exports and adding BrowserTypes --- src/index.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 3c1665350..48631ed93 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,12 @@ import { DesktopAgent } from './api/DesktopAgent'; import * as BridgingTypes from './bridging/BridgingTypes'; +import * as BrowserTypes from './api/BrowserTypes'; +//import in order to create a local export that overwrites type exported via ContextTypes +import * as AppIdentifier from './api/AppIdentifier'; -export * from './api/AppIdentifier'; +export * from './context/ContextTypes'; +export { AppIdentifier }; export * from './api/AppIntent'; export * from './api/AppMetadata'; export * from './api/Channel'; @@ -24,14 +28,17 @@ export * from './api/Methods'; export * from './api/PrivateChannel'; export * from './api/RecommendedChannels'; export * from './api/Types'; +export * from './api/Events' + export * from './context/ContextType'; -export * from './context/ContextTypes'; + export * from './intents/Intents'; -export * from './api/Events' -/* Workaround for conflicts between bridging types and API types + +/* Workaround for conflicts between bridging types, browser type and API types and prettier issue with `export * as`. */ export { BridgingTypes }; +export { BrowserTypes }; declare global { interface Window { From f4f6009a5bfee84502dfcf7aa1a5343c0636f675 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 10:38:53 +0100 Subject: [PATCH 078/152] renaming type declaration for getAgent to GetAgentType To avoid a naming conflict with the implementation --- src/api/GetAgent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index d1e88d711..3f5168df4 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -36,7 +36,7 @@ import { DesktopAgent } from './DesktopAgent'; * //do FDC3 stuff here * }; */ -export type getAgent = ( +export type GetAgentType = ( params?: GetAgentParams, ) => Promise; From a696f6314952d30a7e0aaf96b65bbc385d3f6855 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 10:39:39 +0100 Subject: [PATCH 079/152] correct a type in getAgent example --- src/api/GetAgent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 3f5168df4..8bac2ecaf 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -31,7 +31,7 @@ import { DesktopAgent } from './DesktopAgent'; * getAgent({ * identityUrl: "https://example.com/path?param=appName#example", * channelSelector: false, - * intentresolver: false + * intentResolver: false * }).then((fdc3) => { * //do FDC3 stuff here * }; From e2a8a55b9abb6d408e4aec663976c32a3f177554 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 11:40:33 +0100 Subject: [PATCH 080/152] Fix naming inconsistency in WCP1Hello and WCP3Handshake --- schemas/api/WCP1Hello.schema.json | 2 +- schemas/api/WCP3Handshake.schema.json | 8 ++++---- src/api/BrowserTypes.ts | 12 ++++++------ src/index.ts | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 04db3d4cd..7698986f7 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -41,7 +41,7 @@ "description": "The version of FDC3 API that the app supports.", "type": "string" }, - "resolver": { + "intentResolver": { "title": "Intent Resolver Required", "description": "A flag that may be used to indicate that an intent resolver is or is not required. Set to false if no intents, or only targeted intents, are raised", "type": "boolean" diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json index 07816b085..cbcedb5e8 100644 --- a/schemas/api/WCP3Handshake.schema.json +++ b/schemas/api/WCP3Handshake.schema.json @@ -29,7 +29,7 @@ "type": "string", "description": "The version of FDC3 API that the Desktop Agent will provide support for." }, - "resolver": { + "intentResolverUrl": { "title": "Resolver URL", "description": "Indicates whether an intent resolver UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent will handle another way)", "oneOf": [ @@ -42,7 +42,7 @@ } ] }, - "channelSelector": { + "channelSelectorUrl": { "title": "Channel Selector URL", "description": "Indicates whether a channel selector UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the channel selector (as the Desktop Agent will handle another way)", "oneOf": [ @@ -59,8 +59,8 @@ "additionalProperties": false, "required": [ "fdc3Version", - "resolver", - "channelSelector" + "intentResolverUrl", + "channelSelectorUrl" ] }, "meta": true diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 72d2e62d8..03ebc2804 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -3599,7 +3599,7 @@ export interface WebConnectionProtocol1HelloPayload { * A flag that may be used to indicate that an intent resolver is or is not required. Set to * false if no intents, or only targeted intents, are raised */ - resolver?: boolean; + intentResolver?: boolean; [property: string]: any; } @@ -3672,7 +3672,7 @@ export interface WebConnectionProtocol3HandshakePayload { * `true` to use the default or `false` to disable the channel selector (as the Desktop * Agent will handle another way) */ - channelSelector: boolean | string; + channelSelectorUrl: boolean | string; /** * The version of FDC3 API that the Desktop Agent will provide support for. */ @@ -3682,7 +3682,7 @@ export interface WebConnectionProtocol3HandshakePayload { * `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent * will handle another way) */ - resolver: boolean | string; + intentResolverUrl: boolean | string; } /** @@ -5416,7 +5416,7 @@ const typeMap: any = { { json: "channelSelector", js: "channelSelector", typ: u(undefined, true) }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, { json: "identityUrl", js: "identityUrl", typ: "" }, - { json: "resolver", js: "resolver", typ: u(undefined, true) }, + { json: "intentResolver", js: "intentResolver", typ: u(undefined, true) }, ], "any"), "WebConnectionProtocol2LoadURL": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, @@ -5432,9 +5432,9 @@ const typeMap: any = { { json: "type", js: "type", typ: r("WebConnectionProtocol3HandshakeType") }, ], false), "WebConnectionProtocol3HandshakePayload": o([ - { json: "channelSelector", js: "channelSelector", typ: u(true, "") }, + { json: "channelSelectorUrl", js: "channelSelectorUrl", typ: u(true, "") }, { json: "fdc3Version", js: "fdc3Version", typ: "" }, - { json: "resolver", js: "resolver", typ: u(true, "") }, + { json: "intentResolverUrl", js: "intentResolverUrl", typ: u(true, "") }, ], false), "WebConnectionProtocol4ValidateAppIdentity": o([ { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, diff --git a/src/index.ts b/src/index.ts index 48631ed93..5b50b6abe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import * as BrowserTypes from './api/BrowserTypes'; import * as AppIdentifier from './api/AppIdentifier'; export * from './context/ContextTypes'; + export { AppIdentifier }; export * from './api/AppIntent'; export * from './api/AppMetadata'; @@ -34,7 +35,6 @@ export * from './context/ContextType'; export * from './intents/Intents'; - /* Workaround for conflicts between bridging types, browser type and API types and prettier issue with `export * as`. */ export { BridgingTypes }; From 1d1e53b6752e992112beeb967ebae2e7685edf03 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 12:52:14 +0100 Subject: [PATCH 081/152] Updates to Browser Resident DA specs --- .../api/specs/browserResidentDesktopAgents.md | 59 ++++++++++++------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index b4e2c4901..c680d96c2 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -14,7 +14,7 @@ This document specifies the required behavior for Browser-Resident Desktop Agent This specification only applies to apps running in a browser and therefore assumes use of JavaScript/TypeScript and HTML APIs. Implementations in other languages such as .NET are not covered. -Along with this specification, a new general connection strategy has been established for FDC3 compliant web-applications: FDC3 compliant apps SHOULD make use of `getAent()` function provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) to retrieve their FDC3 interface (an instance of an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or [Preload DAs](./preloadDesktopAgents) without code modification. We refer to this concept as Write Once Run Anywhere (WORA). +Along with this specification, a new general connection strategy has been established for FDC3 compliant web-applications: FDC3 compliant apps SHOULD make use of `getAgent()` function provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) to retrieve their FDC3 interface (an instance of an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or [Preload DAs](./preloadDesktopAgents) without code modification. We refer to this concept as Write Once Run Anywhere (WORA). :::info @@ -60,7 +60,7 @@ and the Desktop Agent application will be found in a 'parent' of the application Browser Resident DAs MUST call `window.addEventListener("message",...)` to receive incoming connection requests from apps, in the form of `WCP1Hello` messages defined in the [Web Connection Protocol](./webConnectionProtocol). -Upon receiving an incoming `WCP1Hello` the DA MUST either: +Upon receiving an incoming `WCP1Hello` the Desktop Agent MUST either: 1) Respond with a `WCP2LoadURL` message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending `WCP1Hello` to the iframe. @@ -74,20 +74,20 @@ All further communication is conducted over the `MessageChannel`. The Desktop Ag The first message received by the Desktop Agent on `MessagePort` `port1` from an application via a `MessageChannel` it created MUST be `WCPValidateAppIdentity`. Once received and successfully processed, the Desktop Agent MUST add handlers to it's `MessagePort` (`port1`) to process [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol.md) messages for this application and then respond with a `WCP5ValidateAppIdentityResponse` message containing an `appId`, `instanceId` and `instanceUuid` to identify the app and this specific instance. If validation fails, it should instead respond with `WCP5ValidateAppIdentityFailedResponse` and close the `MessageChannel`. -App identity is validated and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent. This is achieved by matching `identityUrl`, `actualUrl` fields and `origin` of the `WCPValidateAppIdentity` message (supplied by the application via the `getAgent()` implementation) to the `details.url` field of the known App Directory records. +App identity is validated and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent. This is achieved by matching `identityUrl` (supplied by the application via the `getAgent()` implementation) to the `details.url` field of the known App Directory records. As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the `WCPValidateAppIdentity` message: -- `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. - `identityUrl`: the URL to match to the app directory record. +- `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. -The `actualUrl` field may be used for logging and debug purposes by the Desktop Agent. +Applications _may_ specify the `identityUrl` value as an argument to `getAgent()`. If not specified, the `getAgent()` implementation MUST use the current URL of the application. The Desktop Agent must validate that the origin of the `identityUrl` is the same as the origin of _both_ the `actualUrl` and the `WCPValidateAppIdentity` message sent over the `MessagePort`. The Desktop Agent MUST then match the URL to that of applications known to the Desktop Agent. -Applications _may_ specify the `identityUrl` value are an argument to `getAgent()`. If not specified, the `getAgent()` implementation MUST use the current URL of the application. The Deksotp Agent must validate that the origin of the `identityUrl` is the same as the origin of _both_ the `actualUrl` and the `WCPValidateAppIdentity` message sent over the `MessagePort`. The Desktop Agent SHOULD then match the URL to that of applications known to the Desktop Agent. +The `actualUrl` field may be used for logging and debug purposes by the Desktop Agent and it differing from the `identityUrl` indicates that the application provided an override via `getAgent()`. -Owing to the fact that the different parts of a URL (origin, path, search parameters, anchor) are used differently by different web applications, matching of the `identityUrl` to known application URLs is more complex than a simple string match. To allow application developers control the requirements of matching, Desktop Agents MUST consider a URL to match if all elements (origin, path, search parameters, anchor) present in the App Directory record's URL are present in the `identityUrl`. As multiple App Directory may match a given identity URL, Desktop Agents SHOULD look for the best match that meets the requirements. +Owing to the fact that the different parts of a URL (origin, path, search parameters, anchor) are used differently by web applications, matching of the `identityUrl` to known application URLs can be more complex than a simple string match. To allow application developers control the requirements of matching, Desktop Agents SHOULD consider a URL to match if all elements (origin, path, search parameters, anchor) present in the App Directory record's URL are present in the `identityUrl`. As multiple App Directory records may match a given identity URL, Desktop Agents SHOULD look for the best match that meets the requirements. -For example, given an identity URL `url`, and an array of App Directory records `appDRecords`, a Desktop Agent SHOULD implement matching as follows: +For example, given an identity URL `url`, and an array of App Directory records `appDRecords`, a Desktop Agent MAY implement matching as follows: ```js /** Return the AppD record whose URL best matches the input URL or `null` if no match is found. @@ -191,29 +191,48 @@ For more details on the connection process, please see the documentation for the ## Responding to app communications with Desktop Agent Communication Protocol (DACP) - -See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) - -See bcp.ts for a full list of BCP messages. +After validating an application's identity and any instance identity to be reused, the Desktop Agent is ready to support communication with the application. This is achieved via the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) over the `MessageChannel` established in the previous steps. ## Implementing DAs in hidden iframes -DA providers can leverage hidden iframes to establish a communication mechanism that is independent of Parent windows. This approach allows apps to connect to a DA even when they were not opened by that DA. +As described above, DA providers can leverage hidden iframes to establish a communication mechanism that is independent of a Parent window. This approach allows apps to connect to a DA even when they were not opened by that DA. The hidden iframe url can be provided in two ways: -1) By a Parent DA - This allows DAs to redirect communications to a DA in a hidden iframe at a known location. The main benefit of this approach is that it can allow a system to continue to operate even if the Parent is closed. +1) By a Parent window - This allows DAs to redirect communications to via a hidden iframe that loads a known URL. The main benefit of this approach is that it can allow a system to continue to operate even if the parent window is closed. + +2) By a `failover` function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can create a hidden iframe and return a reference to it (a `WindowProxy`) to initiate communication with via the WCP and DACP in the same way as we do with a parent window. Alternatively, a `DesktopAgent` implementation maybe loaded directly and returned from the failover function, which `getAgent()` will pass-through. + +## Channel Selector and Intent Resolver User Interfaces + +Channel Selector and Intent Resolver user-interfaces are normally provided by Desktop Agents to applications. However, when running in a web browser a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()` and it may be challenging to display a secondary window over the application when needed (due to pop-up blocking and user preferences). -2) By a failover function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can provide a url. In such a scenario, failover is instantaneous because the absence of Parent windows is deterministic. Note that this approach may compromise Write Once Run Anywhere (WORA) unless apps use dynamic mechanisms to determine the hidden url (such as through clues provided in their href.) +The `getAgent()` implementation can facilitate the injection and management of iframes in an application window. An app may use the optional `channelSelector` and `intentResolver` parameters to the `getAgent()` to indicate whether or not they need these interfaces (for example they may not raise intents or may provide their own resolver UI based on the DA API's `findIntent` or `findIntentsForContext` functions so they don't need a DA-provided interface). These parameters are forwarded onto the Desktop Agent in the `WCP1Hello` connection message. -The same WCP mechanism is used to connect to DAs located in hidden iframes. Likewise, the same BCP messages are used for communications. +Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is be able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. -## UI Channel Selector and Intent Resolver +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. -A DA may implement its own Channel Selector and Intent Resolver or may utilize the one provided by "@finos/fdc3" via `getAgent()` ("built-in UI"). For instance, a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()`. The built-in UI can be leveraged in this circumstance. Send the `BCPResolveIntent` and `BCPInitializeChannelSelector` messages to invoke the built-in UI. +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`. + +See the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) for more details. ## Disconnects -DAs are responsible for tracking when app windows close by checking `win.closed` in a loop. +DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. + +::: info + +The HTML Standard specifies an [onclose event handler on `MessagePort`](https://html.spec.whatwg.org/multipage/web-messaging.html#handler-messageport-onclose) which would provide an ideal event-based solution for tracking the closing of app windows. However, this event is not currently implemented in Chrome/Chromium due to security concerns (it reveals the garbage collection activity of the process holding the other end of the pipe, see comment on [whatwg/html/issues/1766](https://github.com/whatwg/html/issues/1766#issuecomment-1958782062), see also proposals to [restrict when MessagePort's onclose event can fire](https://github.com/whatwg/html/issues/10201)). + +::: + +Checking whether an application has closed may be achieved by a number of approaches: -https://stackoverflow.com/questions/9388380/capture-the-close-event-of-popup-window-in-javascript/48240128#48240128 \ No newline at end of file +- By checking the `closed` property of WindowProxy objects that were received via the `source` property of the original `WCP1Hello` message, or any subsequent message over the `MessageChannel`. `closed` will be true if the window or frame was closed or destroyed, or the window or frame has navigated cross-domain. + - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. + - Hence, if an equivalent `WindowProxy` object is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. +- By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation SHOULD attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. + - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. +- By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. + - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. From dd81ea6b4a3daa8534fb8927e7a4384b625a24ad Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 12:56:07 +0100 Subject: [PATCH 082/152] correcting admonition in browser specs --- docs/api/specs/browserResidentDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index c680d96c2..dd14cc023 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -221,7 +221,7 @@ See the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtoc DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. -::: info +:::info The HTML Standard specifies an [onclose event handler on `MessagePort`](https://html.spec.whatwg.org/multipage/web-messaging.html#handler-messageport-onclose) which would provide an ideal event-based solution for tracking the closing of app windows. However, this event is not currently implemented in Chrome/Chromium due to security concerns (it reveals the garbage collection activity of the process holding the other end of the pipe, see comment on [whatwg/html/issues/1766](https://github.com/whatwg/html/issues/1766#issuecomment-1958782062), see also proposals to [restrict when MessagePort's onclose event can fire](https://github.com/whatwg/html/issues/10201)). From 59c3d0d87361062abc1e5dc5b0f33c7a7f0b57bf Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 12:58:03 +0100 Subject: [PATCH 083/152] another admonition + moving disconnects into WCP section of Browser specs --- .../api/specs/browserResidentDesktopAgents.md | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index dd14cc023..304bf4c90 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -181,7 +181,7 @@ If an `instanceId` and `instanceUuid` are not provided or do not pass the chjeck Details of the received `instanceId` and `instanceUuid` are stored by the `getAgent()` implementation in [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) within the application window and are automatically reused when reconnecting to the Desktop Agent. SessionStorage is used as it is scoped to a particular window (rather than origin, as is the case with LocalStorage), allowing separate storage for each app instance. -:::warn +:::warning Apps launched via a call to `window.open()` from a window or app instance on the same origin can appear to be the original window. This is because browsers may clone SessionStorage for newly opened windows. When a child window calls `getAgent()` with the same `appId`, `instanceId` and `instanceUuid` as the parent window, it will appear to the DA that a navigation event occurred on the parent window. DAs therefore MUST also compare the `WindowProxy` object that is used to establish each connection to differentiate such cloned instances. If the WindowProxy objects do not match, then a new `instanceId` and `instanceUuid` MUST be assigned. @@ -189,6 +189,26 @@ Apps launched via a call to `window.open()` from a window or app instance on the For more details on the connection process, please see the documentation for the [Web Connection Protocol](./webConnectionProtocol). +### Disconnects + +DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. + +:::info + +The HTML Standard specifies an [onclose event handler on `MessagePort`](https://html.spec.whatwg.org/multipage/web-messaging.html#handler-messageport-onclose) which would provide an ideal event-based solution for tracking the closing of app windows. However, this event is not currently implemented in Chrome/Chromium due to security concerns (it reveals the garbage collection activity of the process holding the other end of the pipe, see comment on [whatwg/html/issues/1766](https://github.com/whatwg/html/issues/1766#issuecomment-1958782062), see also proposals to [restrict when MessagePort's onclose event can fire](https://github.com/whatwg/html/issues/10201)). + +::: + +Checking whether an application has closed may be achieved by a number of approaches: + +- By checking the `closed` property of WindowProxy objects that were received via the `source` property of the original `WCP1Hello` message, or any subsequent message over the `MessageChannel`. `closed` will be true if the window or frame was closed or destroyed, or the window or frame has navigated cross-domain. + - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. + - Hence, if an equivalent `WindowProxy` object is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. +- By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation SHOULD attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. + - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. +- By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. + - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. + ## Responding to app communications with Desktop Agent Communication Protocol (DACP) After validating an application's identity and any instance identity to be reused, the Desktop Agent is ready to support communication with the application. This is achieved via the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) over the `MessageChannel` established in the previous steps. @@ -216,23 +236,3 @@ Communication between the `DesktopAgentProxy` and the iframes it injects is achi 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`. See the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) for more details. - -## Disconnects - -DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. - -:::info - -The HTML Standard specifies an [onclose event handler on `MessagePort`](https://html.spec.whatwg.org/multipage/web-messaging.html#handler-messageport-onclose) which would provide an ideal event-based solution for tracking the closing of app windows. However, this event is not currently implemented in Chrome/Chromium due to security concerns (it reveals the garbage collection activity of the process holding the other end of the pipe, see comment on [whatwg/html/issues/1766](https://github.com/whatwg/html/issues/1766#issuecomment-1958782062), see also proposals to [restrict when MessagePort's onclose event can fire](https://github.com/whatwg/html/issues/10201)). - -::: - -Checking whether an application has closed may be achieved by a number of approaches: - -- By checking the `closed` property of WindowProxy objects that were received via the `source` property of the original `WCP1Hello` message, or any subsequent message over the `MessageChannel`. `closed` will be true if the window or frame was closed or destroyed, or the window or frame has navigated cross-domain. - - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. - - Hence, if an equivalent `WindowProxy` object is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. -- By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation SHOULD attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. - - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. -- By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. From a161739175f61bae42a9968907635dce50a1d65a Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 5 Sep 2024 14:04:19 +0100 Subject: [PATCH 084/152] Clarifying disconnect content --- docs/api/specs/browserResidentDesktopAgents.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 304bf4c90..8e7facf1f 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -202,13 +202,17 @@ The HTML Standard specifies an [onclose event handler on `MessagePort`](https:// Checking whether an application has closed may be achieved by a number of approaches: - By checking the `closed` property of WindowProxy objects that were received via the `source` property of the original `WCP1Hello` message, or any subsequent message over the `MessageChannel`. `closed` will be true if the window or frame was closed or destroyed, or the window or frame has navigated cross-domain. - - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. - - Hence, if an equivalent `WindowProxy` object is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. + - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. Hence, checking the `closed` property will not catch all cases. + - If an equivalent `WindowProxy` object (`WindowProxy` objects can be compared with `==` and will be equivalent if they represent the same window) is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. - By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation SHOULD attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. - By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. +Finally, Desktop Agents SHOULD retain instance details for applications that have closed as they may appear to close during navigation events, or may navigate away and then navigate back. By retaining the instance data (`instanceId`, `instanceUuid` and `WindowProxy`) the same instance identity can be maintained or reissued. There is no standard length of time that such details should be retained, hance, Desktop Agents MAY determine for themselves how long to retain instance details for closed instances. + + + ## Responding to app communications with Desktop Agent Communication Protocol (DACP) After validating an application's identity and any instance identity to be reused, the Desktop Agent is ready to support communication with the application. This is achieved via the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) over the `MessageChannel` established in the previous steps. From 99918b21950591d1829dbd92cd577feb57384205 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 6 Sep 2024 11:52:57 +0100 Subject: [PATCH 085/152] Update glossary references and intro to Web Connection protocol --- docs/api/specs/webConnectionProtocol.md | 25 +++++++++++-------------- docs/fdc3-glossary.md | 8 +++++++- docs/references.md | 6 +++++- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 05eb82cd3..9343ad312 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -4,29 +4,26 @@ sidebar_label: Web Connection Protocol title: FDC3 Web Connection Protocol (next) --- -`getAgent()` is implemented in the `@finos/fdc3` library. This specification details how it retrieves and provides FDC3 interface object. +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a `getAgent()` function in the `@finos/fdc3` library, which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object. -## Definition of Terms +:::tip -**Browser Resident Desktop Agent (DA)**: A Desktop Agent loaded in a window or frame within a web browser (in contrast to a Desktop Agent that "injects" a global fdc3 object). +See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. -> Note - Throughout this document, when referring to "DA" without any other qualification we mean a Browser-Resident Desktop Agent. +::: -**getAgent()**: The library function provided by `@finos/fdc3` that discovers and establishes communication to DAs. It may (1) return a reference to an injected `DesktopAgent` instance, (2) use the FDC3 Web Connection Protocol (WCP) to discover a DA (e.g. in a "parent" window or frame) and return a `DesktopAgent` instance that communicates with the DA using the FDC3 Desktop Agent Communication Protocol (DACP), or (3) run an application provided failover function that provides direct or indirect access to a `DesktopAgent`. +The WCP supports both interfaces to web-based Desktop Agents defined in the FDC3 Standard: -**Web Connection Protocol (WCP)**: A protocol for discovering and establishing communications with a DA. Ths includes a prescribed algorithm as well as some standard messages that are transmitted using `window.postMessage`. +- **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. +- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. -**Desktop Agent Communication Protocol (DACP)**: A protocol that uses the standard HTML Channel Messaging API (MessagePort) to communicate with a DA in a remote iframe or window via messages. +The WCP allows FDC3-enabled application to detect which FDC3 web-interface is present and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). -**Parent**: A browser window or frame that creates the window or iframe in which an application runs. (The parent provides a `WindowProxy` object which is used by WCP.) +:::tip -**WindowProxy**: An interface defined by the HTML standard that proxies a remote Window object: https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-windowproxy-exotic-object. This is used by WCP. +See the FDC3 [Glossary](../../fdc3-glossary) and [References](../../references.md) pages for definitions of terms and links to external APIs used throughout this document. -**MessagePort**: An interface defined by the HTML Channel Messaging standard that can be used for cross-domain communication between windows. This is used by BCP. - -**Flux Standard Action (FSA)**: An [industry standard](https://github.com/redux-utilities/flux-standard-action) JSON envelope. All WCP and BCP messages are defined as FSAs. - -**SessionStorage**: A JavaScript storage API that persists data for web apps (segregated by domain) within a specific window or tab (SessionStorage is identical to the localStorage API but is tied to a particular window/tab, as defined by the HTML Standard: https://html.spec.whatwg.org/multipage/webstorage.html#dom-sessionstorage.) +::: ## Establishing Connectivity Using the Web Connection Protocol (WCP) diff --git a/docs/fdc3-glossary.md b/docs/fdc3-glossary.md index ac161dca7..db7416cce 100644 --- a/docs/fdc3-glossary.md +++ b/docs/fdc3-glossary.md @@ -22,6 +22,7 @@ For the purposes of this Standard, the following definitions apply. Other terms - **bridging**: Shorthand for the exchange of messages across a Desktop Agent Bridge for the purposes of extending interop between apps managed by different Desktop Agents. - **Bridge Connection Protocol (BCP)**: A defined set of steps for a Desktop Agent to connect to a Desktop Agent Bridge. - **Bridge Messaging Protocol (BMP)**: Protocol for Desktop Agents to communicate with each other over a Desktop Agent Bridge. +**Browser Resident Desktop Agent (DA)**: A Desktop Agent loaded in a window or frame within a web browser (in contrast to a Desktop Agent that "injects" a global fdc3 object) that is communicated with via a Desktop Agent Proxy. - **Channel**: A grouping of apps for the purposes of sharing stateful pieces of data. A secondary interface of the FDC3 API. - **context channels**: A mechanism to allow sets of apps to share stateful pieces of data among themselves, and to be alerted when that data changes. - **context**: Shorthand for context data. @@ -30,18 +31,23 @@ For the purposes of this Standard, the following definitions apply. Other terms - **Desktop Agent Bridge (DAB)**: An independent service that Desktop Agents connect to which allows them to relay requests to other Desktop Agents also connected to the bridge, allowing FDC3-based interop to extend across multiple Desktop Agents and machines. - **Desktop Agent Communication Protocol (DACP)**: JSON communication protocol for the Desktop Agent API. Used by a Desktop Agent Proxy interface over the Channel Messaging API (as specified by the Web Connection Protocol) to communicate with a Desktop Agent running in another window/frame. - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3` and may be returned by the `getAgent()` function defined in the FDC3 Web Connection Protocol. -- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Web Connection Protocol and Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. Returned by the `getAgent()` function provided by the FDC3 NPM module where a browser-based Desktop Agent is detected. +- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Web Connection Protocol and Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. Returned by the `getAgent()` function provided by the FDC3 NPM module where a browser-based Desktop Agent is detected. - **FDC3 API**: A baseline, consistent developer interface for interoperability between applications. - **GUID**: Globally Unique IDentifier, synonymous with UUID. Defined by [IETF RFC4122](references). - **interop**: Shorthand for interoperability. - **interoperability**: the ability of software applications to exchange and make use of information and invoke specified actions. - **intent**: A verb, with a pre-agreed meaning (expected behavior), used to invoke an action between applications. A set of such verbs can, in conjunction with Context Data acting as nouns, be used to put together common cross-application workflows on the financial desktop. - **Listener**: API interface which allows unsubscribing from Intents or Context Channels. +- **MessageChannel**: An interface defined by the [HTML Standard's Channel Messaging API](https://html.spec.whatwg.org/multipage/web-messaging.html#channel-messaging) for a two-way pipe with a `MessagePort` at each end, used to implement communication between code running in different browsing contexts. Used by the Web Connection Protocol. +- **MessagePort**: An interface defined by the [HTML Standard's Channel Messaging API](https://html.spec.whatwg.org/multipage/web-messaging.html#channel-messaging) used to transmit and receive messages over a `MessageChannel`. Each channel has two message ports. Data sent through one port is received by the other port, and vice versa. Used by the Web Connection Protocol. - **Originating App**: The application that sent a particular context message or raised an intent. +- **Parent frame**: A browser window or frame that creates the window or iframe in which an application runs. (The parent provides a `WindowProxy` object which is used by Web Connection Protocol). - **Platform Provider**: An environment that provides an implementation of the FDC3 API that applications can use. - **raising an intent**: The act of requesting, via the FDC3 API/Desktop Agent that a specified action be performed by another application, using specified context data as input. - **resolver**: A facility of a Desktop Agent used to map a raised intent and associated context object to an application that will perform the action represented by the intent, using the context object as input. Where multiple applications can resolve the intent, a resolver will often display a user-interface allowing a user to pick from the available applications that support the intent and type of context supplied. - **resolving an intent**: The act of mapping a specified intent and context object to an application. +**SessionStorage**: A JavaScript storage API defined by the HTML Standard's Web Storage API](https://html.spec.whatwg.org/multipage/webstorage.html#webstorage) that persists data for web apps within the scope of a specific window or tab. SessionStorage is identical to the LocalStorage API but is scoped to a particular window/tab, as defined by the HTML Standard: - **standard intent**: An intent defined by this Standard. - **UUID**: Universally Unique IDentifier, synonymous with GUID. Defined by [IETF RFC4122](references). - **Web Connection Protocol (WCP)**: A defined set of steps for a web application to connect to Desktop Agents that implement any of the interfaces defined for web applications in the FDC3 Standard, including both preloaded Desktop Agent interfaces and Browser-based Desktop Agents that work with a Desktop Agent Proxy. +- **WindowProxy**: An interface defined by the HTML standard that proxies a remote Window object: . WindowProxy objects are used for communication via `postMessage` as part of the Web Connection Protocol. \ No newline at end of file diff --git a/docs/references.md b/docs/references.md index 379c5342d..da23f8cf7 100644 --- a/docs/references.md +++ b/docs/references.md @@ -8,8 +8,12 @@ sidebar_label: References The following normative documents contain provisions, which, through reference in this text, constitute provisions of this Standard. For dated references, subsequent amendments to, or revisions of, any of these publications do not apply. However, parties to agreements based on this Standard are encouraged to investigate the possibility of applying the most recent editions of the normative documents indicated below. For undated references, the latest edition of the normative document referred to applies: - **Apache 2.0 open-source license**, . -- **Community Specification license**, +- **Community Specification license**, . +- **HTML Living Standard**, . +- **HTML Standard's Channel Messaging API**, . +- **HTML Standard's Web Storage API**, . - **ISO 3166-1**, _Codes for the representation of names of countries and their subdivisions – Part 1: Country codes_, . +- **ISO 4217:2015**, _Codes for the representation of currencies_, . - **ISO 8601-1:2019**, _Date and time — Representations for information interchange — Part 1: Basic rules_, - **JSON Schema**, . - **OpenAPI Standard v3.0**, . From 2cf84b41b9347f8e920966021f4f2d2f6da7770c Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 9 Sep 2024 18:45:01 +0100 Subject: [PATCH 086/152] WIP Web Connection Protocol (WCP) documentation --- docs/api/ref/GetAgent.md | 42 +--- .../api/specs/browserResidentDesktopAgents.md | 26 +- .../desktopAgentCommunicationProtocol.md | 6 + docs/api/specs/webConnectionProtocol.md | 226 +++++++++++++----- docs/context/ref/ChatRoom.md | 1 + schemas/api/common.schema.json | 2 +- src/api/GetAgent.ts | 2 +- 7 files changed, 192 insertions(+), 113 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 0f7e13c5a..80e3675a4 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -68,7 +68,7 @@ A small number of arguments are accepted that can affect the behavior of `getAge * * @property {number} timeout Number of milliseconds to allow for an fdc3 * implementation to be found before calling the failover function or - * rejecting (default 1000). Note that the timeout is cancelled as soon as a + * 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. * @@ -114,33 +114,7 @@ As web applications can navigate to or be navigated by users to different URLs a ::: -Finally, if there is still no Desktop Agent available, or an issue prevent connection to it, the `getAgent()` function will reject its promise with a message from the `AgentError` enumeration. - -```ts -/** - * Contains constants representing the errors that can be encountered when - * trying to connect to a web-based Desktop Agent with the getAgent function. - */ -enum AgentError { - /** Returned if no Desktop Agent was found by any means available or - * if the Agent previously connected to is not contactable on a - * subsequent connection attempt.*/ - AgentNotFound = "AgentNotFound", - - /** Returned if validation of the app identity by the Desktop Agent - * Failed or the app is not being allowed to connect to the Desktop Agent - * for another reason. */ - AccessDenied = "AccessDenied", - - /** Returned if an error or exception occurs while trying to set - * up communication with a Desktop Agent. */ - ErrorOnConnect = "ErrorOnConnect", - - /** Returned if either the failover function itself, or what it returned, - * was not the right type. */ - InvalidFailover = "InvalidFailover" -} -``` +Finally, if there is still no Desktop Agent available, or an issue prevent connection to it, the `getAgent()` function will reject its promise with a message from the [`AgentError`](./Errors#agenterror) enumeration. ## Failover function @@ -166,18 +140,16 @@ If you wish to _completely override FDC3s standard mechanisms_, then do not use ::: -Failover functions MUST be asynchronous MUST resolve to one of the following types: +Failover functions MUST be asynchronous and MUST resolve to one of the following types: 1. [`DesktopAgent`](./DesktopAgent) - The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. -2. [`WindowProxy`](https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-windowproxy-exotic-object) ([MDN](https://developer.mozilla.org/en-US/docs/Glossary/WindowProxy)) - The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Ensure that the iframe has loaded (listen for the `load` event) or for separate windows allow a suitable timeout for the window load, then resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. - - + The application may choose to directly import or load code that provides a `DesktopAgent` implementation. `getAgent()` will then resolve to the provided `DesktopAgent`. +2. [`WindowProxy`](https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-windowproxy-exotic-object) ([MDN](https://developer.mozilla.org/en-US/docs/Glossary/WindowProxy)) + The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Ensure that the iframe has loaded (listen for the `load` event) or for separate windows allow a suitable timeout for the window load, then resolve to the `WindowProxy` object for the window or iframe. The `getAgent()` call will then use the supplied `WindowProxy` to establish a connection. ## Persisted Connection Data -The `getAgent()` function uses [`SessionStorage`](https://html.spec.whatwg.org/multipage/webstorage.html) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instance Id, in case it navigates or is refreshed. Applications are not expected to interact with this information directly, rather it is set and used by the `getAgent()` implementation. +The `getAgent()` function uses [`SessionStorage`](https://html.spec.whatwg.org/multipage/webstorage.html) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) to persist information on an instance of an app under the key `"FDC3-Desktop-Agent-Details"` and how it connected to a Desktop Agent in order to ensure a consistent connection type and `instanceId` when reconnecting after navigation or refresh events. Applications are not expected to interact with this information directly, rather it is set and used by the `getAgent()` implementation. The details persisted conform to the following type: diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 8e7facf1f..70623e7f2 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -6,7 +6,7 @@ title: Browser Desktop Agents (next) :::info _[@experimental](../fdc3-compliance#experimental-features)_ -Browser Resident Desktop Agents are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in the future version and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +Browser Resident Desktop Agents are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in future versions and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. ::: @@ -58,25 +58,25 @@ and the Desktop Agent application will be found in a 'parent' of the application ## Responding to app instance connections - Web Connection Protocol (WCP) -Browser Resident DAs MUST call `window.addEventListener("message",...)` to receive incoming connection requests from apps, in the form of `WCP1Hello` messages defined in the [Web Connection Protocol](./webConnectionProtocol). +Browser Resident DAs MUST call `window.addEventListener("message",...)` to receive incoming connection requests from apps, in the form of [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) messages defined in the [Web Connection Protocol](./webConnectionProtocol). -Upon receiving an incoming `WCP1Hello` the Desktop Agent MUST either: +Upon receiving an incoming [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) the Desktop Agent MUST either: -1) Respond with a `WCP2LoadURL` message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). - - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending `WCP1Hello` to the iframe. +1) Respond with a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). + - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) to the iframe. 2) Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. - - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a `WCPValidateAppIdentity` message from the application. - - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a `WCP3Handshake` message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel`` to the message. + - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message from the application. + - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel` to the message. -All further communication is conducted over the `MessageChannel`. The Desktop Agent should consider the newly created port to be inactive until a `WCPValidateAppIdentity` message is received via the MessagePort and successfully processed. +All further communication is conducted over the `MessageChannel`. The Desktop Agent should consider the newly created port to be inactive until a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message is received via the `MessagePort` and successfully processed. ### Validating app identity -The first message received by the Desktop Agent on `MessagePort` `port1` from an application via a `MessageChannel` it created MUST be `WCPValidateAppIdentity`. Once received and successfully processed, the Desktop Agent MUST add handlers to it's `MessagePort` (`port1`) to process [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol.md) messages for this application and then respond with a `WCP5ValidateAppIdentityResponse` message containing an `appId`, `instanceId` and `instanceUuid` to identify the app and this specific instance. If validation fails, it should instead respond with `WCP5ValidateAppIdentityFailedResponse` and close the `MessageChannel`. +The first message received by the Desktop Agent on `MessagePort` `port1` from an application via a `MessageChannel` it created MUST be [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json). Once received and successfully processed, the Desktop Agent should respond with a [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing an `appId`, `instanceId` and `instanceUuid` to identify the app and this specific instance and then enable processing of [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol.md) messages for this application. If validation fails, it should instead respond with [`WCP5ValidateAppIdentityFailedResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) and close the `MessageChannel`. App identity is validated and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent. This is achieved by matching `identityUrl` (supplied by the application via the `getAgent()` implementation) to the `details.url` field of the known App Directory records. -As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the `WCPValidateAppIdentity` message: +As web applications may vary their URL during use, or serve multiple applications from the same origin (differentiated by path, search params and/or hash), care must be taken in matching URLs to appD records. Two URLs are sent to the Desktop Agent in the [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message: - `identityUrl`: the URL to match to the app directory record. - `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. @@ -175,9 +175,9 @@ let matchUrlToAppD = (url, appDRecords) => { ### Validating instance identity -If the `WCPValidateAppIdentity` request message contains `instanceId` and `instanceUuid` fields then the window may represent an app instance that has navigated or refreshed and requires reconnecting with the previously assigned `instanceId`. The DA SHOULD reissue the same instanceId if the `instanceUuid`, `appId`, `WindowProxy` object and origin provided **all** match what is already on record and return details via the `WCP5ValidateAppIdentityResponse` message. +If the [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) request message contains `instanceId` and `instanceUuid` fields then the window may represent an app instance that has navigated or refreshed and requires reconnecting with the previously assigned `instanceId`. The DA SHOULD reissue the same instanceId if the `instanceUuid`, `appId`, `WindowProxy` object and origin provided **all** match what is already on record and return details via the [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message. -If an `instanceId` and `instanceUuid` are not provided or do not pass the chjecks defined above, a new `instanceId` and `instanceUuid` should be generated by the Desktop Agent and returned in the `WCP5ValidateAppIdentityResponse` message. All generated `instanceId` and `instanceUuid` values should be retained by the DA (for the session or other appropriate period of time), along with a reference to the `WindowProxy` (`event.source` from the initial message received), as these can later be used for comparison to help determine if a new connection request is coming from a previously connected window which may or may not represent an existing app instance. If a previously connected window is reconnecting, any existing `MessageChannel` instance created for it can be cleaned up. +If an `instanceId` and `instanceUuid` are not provided or do not pass the checks defined above, a new `instanceId` and `instanceUuid` should be generated by the Desktop Agent and returned in the [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message. All generated `instanceId` and `instanceUuid` values should be retained by the DA (for the session or other appropriate period of time), along with a reference to the `WindowProxy` (`event.source` from the initial message received), as these can later be used for comparison to help determine if a new connection request is coming from a previously connected window which may or may not represent an existing app instance. If a previously connected window is reconnecting, any existing `MessageChannel` instance created for it can be cleaned up. Details of the received `instanceId` and `instanceUuid` are stored by the `getAgent()` implementation in [SessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) within the application window and are automatically reused when reconnecting to the Desktop Agent. SessionStorage is used as it is scoped to a particular window (rather than origin, as is the case with LocalStorage), allowing separate storage for each app instance. @@ -211,8 +211,6 @@ Checking whether an application has closed may be achieved by a number of approa Finally, Desktop Agents SHOULD retain instance details for applications that have closed as they may appear to close during navigation events, or may navigate away and then navigate back. By retaining the instance data (`instanceId`, `instanceUuid` and `WindowProxy`) the same instance identity can be maintained or reissued. There is no standard length of time that such details should be retained, hance, Desktop Agents MAY determine for themselves how long to retain instance details for closed instances. - - ## Responding to app communications with Desktop Agent Communication Protocol (DACP) After validating an application's identity and any instance identity to be reused, the Desktop Agent is ready to support communication with the application. This is achieved via the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol) (DACP) over the `MessageChannel` established in the previous steps. diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index f5000dea0..2be68ab96 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -6,6 +6,12 @@ title: Desktop Agent Communication Protocol (next) # Desktop Agent Communication Protocol (DACP) +:::info _[@experimental](../fdc3-compliance#experimental-features)_ + +The Desktop Agent Communication Protocol (DACP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. + +::: + DACP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. :::note diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 9343ad312..c6ecaa66b 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -4,128 +4,230 @@ sidebar_label: Web Connection Protocol title: FDC3 Web Connection Protocol (next) --- -The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a `getAgent()` function in the `@finos/fdc3` library, which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object. +:::info _[@experimental](../fdc3-compliance#experimental-features)_ + +The FDC3 Web Connection Protocol (WCP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. + +::: + +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the `@finos/fdc3` library, which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. :::tip -See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. +The @finos/fdc3 npm module provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. ::: The WCP supports both interfaces to web-based Desktop Agents defined in the FDC3 Standard: - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. -- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window. +- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window, via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). The WCP allows FDC3-enabled application to detect which FDC3 web-interface is present and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). :::tip -See the FDC3 [Glossary](../../fdc3-glossary) and [References](../../references.md) pages for definitions of terms and links to external APIs used throughout this document. +See the FDC3 [Glossary](../../fdc3-glossary) and [References](../../references.md) pages for definitions of terms and links to external APIs used throughout this document. ::: -## Establishing Connectivity Using the Web Connection Protocol (WCP) - -The WCP algorithm (coordinated between `getAgent()` and DAs) has four steps. Each step may contain sub-steps. - -1. Connect to DA -2. Validate app identity -3. Persist DesktopAgentDetails to SessionStorage -4. Resolve promise with DesktopAgent interface - -### Connect to DA (Step 1) - -1) Check for a possible navigation or refresh event - - Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDetails` record. If it exists **and the appId matches**, then a navigation or refresh event has occurred and the stored data MUST be used to reestablish a connection to the DA. This ensures that the window maintains a consistent `instanceId` between navigation events within an app. If it doesn't exist, or the appId does not match, then proceed to step (2). +:::tip - See [DesktopAgentDetails](./getAgent.d.ts). +Further details for implementing Preload Desktop Agents (which use a Desktop Agent Preload interface) or a Browser Resident Desktop Agent (which use a Desktop Agent Proxy interface) are available in the [Preload Desktop Agent](./preloadDesktopAgents) or [Browser Resident Desktop Agent Specification](./browserResidentDesktopAgents), respectively. - Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `url` exists (immediately opening a hidden iframe). +::: - If use of persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with "ReestablishConnectionFailed". +## Establishing Connectivity Using the Web Connection Protocol (WCP) -2) Create a race between these four mutually exclusive threads: +The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps which must be completed within the window of the web application trying to connect. Each step may contain sub-steps. + +1. Locate a Desktop Agent interface +2. Validate app & instance identity +3. Persist connection details to SessionStorage +4. Return DesktopAgent interface + +### Step 1: Locate a Desktop Agent interface + +#### 1.1 Check for a possible navigation or refresh event + +Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDetails` record. If it exists, then a navigation or refresh event may have occurred and the stored data MUST be sent when attempting to establish a connection to the DA. This ensures that the window can maintain a consistent `instanceId` between navigation or refresh events within an app. If it doesn't exist then proceed to step (2). + +Any data stored under the `FDC3-Desktop-Agent-Details` must conform to the [DesktopAgentDetails](../ref/GetAgent#persisted-connection-data) type. + +Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `agentUrl` exists, indicating that the connection should be establish by loading the URL into a hidden iframe and initiating communication with that instead. + +If use of the persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with `AgentNotFound` error from the [`AgentError`](../ref/Errors#agenterror) enumeration. + +#### 1.2 Desktop Agent discovery + +Next, attempt to discover whether Desktop Agent Preload or Desktop Agent Proxy interfaces are available, within a specified timeout. The optional `params.timeout` argument to `getAgent()` allows an application to specify the timeout that should be used, with a default value of 750ms. Discovery of Desktop Agent Preload or Desktop Agent Proxy interfaces should be conducted in parallel where possible and the timeout cancelled as soon as an interface is discovered. If a `DesktopAgentDetails` record was found in the previous step, limit discovery to the interface specified, or, if an `agentUrl` property was specified in the `DesktopAgentDetails` record, skip the discovery step entirely and proceed to the next step. + +To discover a Desktop Agent Preload interface, check for the presence of an `fdc3` object in the global scope (i.e. `window.fdc3`). If it exists return it immediately. If it does not exist then add a listener for the global `fdc3Ready` event with the the specified timeout, e.g.: + +```ts +const discoverPreloadDA = async (timeout): Promise => { + return new Promise((resolve, reject) => { + // if the global is already available resolve immediately + if (window.fdc3) { + resolve(window.fdc3); + } else { + //`fdc3Ready` event listener function + const listener = () => { + clearTimeout(timeout); + if (window.fdc3) { + clearTimeout(timeout); + resolve(window.fdc3); + } else { + reject('The `fdc3Ready` event fired, but `window.fdc3` Was not set!'); + } + }; + // Setup a timeout to return a rejected promise + const timeout = setTimeout( + () => { + //clear the event listener to ignore a late event + window.removeEventListener('fdc3Ready', listener); + if (window.fdc3){ + resolve(window.fdc3); + } else { + reject('Desktop Agent Preload not found '); + } + }, + timeout + ); + // listen for the fdc3Ready event + window.addEventListener('fdc3Ready', listener, { once: true }); + } + }); +}; +``` + +To discover a Desktop Agent Proxy interface, locate all candidates for a `parent` window or frame by first checking the values of `window.opener` and `window.parent`. If `window.opener !== null` or `window.parent !== window` then they are candidates for a parent window or frame. As iframes can be nested we can also search for candidates that are parents of a parent frame, e.g.: + +```ts +const discoverProxyCandidates = (): WindowProxy[] => { + const candidates = []; + //parent window + if (!!window.opener) { candidates.push(window.opener); } + + //parent frames + let currentWin = window; + while (currentWin.parent !== currentWin){ + candidates.push(currentWin.parent); + currentWin = currentWin.parent; + } - **a) Timeout of 750ms** or the value provided by the `timeout` field + //parent window of top-level parent frame + if (window !== currentWin && !!currentWin.opener) { + candidates.push(currentWin.opener); + } + return candidates; +} +``` - **b) Preload DAs:** +Setup a timer for specified timeout, and then for each `candidate` found, attempt to establish communication with it as follows: - If a global (window or globalThis) `fdc3` object exists then return it immediately. If it does not exist then wait for the `fdc3Ready` event. After the `fdc3Ready` event triggers, recheck for the existence of the global `fdc3` object and return it if found. + 1. Add a listener (`candidate.addEventListener("message", (event) => {})`) to receive response messages from the `candidate`. + 2. Send a [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) message to it via `postMessage`. - **c) Browser-resident DAs:** + ```ts + const hello = { + type: "WCP1Hello", + payload: { + identityUrl: identityUrl, + actualUrl: actualUrl, + fdc3Version: "2.2", + intentResolver: true + channelSelector: true + }, + meta: { + connectionAttemptUuid: "bc96f1db-9b2b-465f-aab3-3870dc07b072" + timestamp: "2024-09-09T11:44:39+00:00" + } + }; + candidate.postMessage(hello, { targetOrigin: * };) + ``` - If a `url` exists from step (1) then proceed directly to step (iv), otherwise; + Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. + 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. + 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: + * Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: + ```ts + const loadIframe = (url, loadedHandler): WindowProxy => { + const ifrm = document.createElement("iframe"); + iframe.onload = loadedHandler; + ifrm.src = url; + ifrm.style.width = "0"; + ifrm.style.height = "0"; + ifrm.style.visibility = "0"; + ifrm.ariaHidden="true"; + document.body.appendChild(ifrm); + return ifrm.contentWindow; + } + ``` + * Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. + 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have be received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. + + Add a listener (`port.addEventListener("message", (event) => {})`) to receive messages from the selected `candidate`, before moving on to the next stage. + 6. If no candidates were found or no [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) has been received by the time that the timeout expires, then neither a Desktop Agent Preload or Desktop Agent Proxy interface has been discovered. If this occurs, the `getAgent()` implementation will run any `failover` function provided as a parameter to `getAgent()`, allowing the application to provide an alternative means of connecting to or starting up a Desktop Agent. + + An async failover function may resolve to either a `DesktopAgent` implementation or the application may create either an iframe or open a new window, load an appropriate URL for a Desktop Agent implementation and resolve to its `WindowProxy` reference (e.g. `iframe.contentWindow` or the result of a call to `window.open(...)`). + + If the failover function resolves to a `DesktopAgent` implementation it should be immediately returned in the same way as a `window.fdc3` reference was and handled as if it represents a Desktop Agent Preload interface. - Locate all "Parents" (`window.opener`, `window.parent` or `window.parent.opener`). If no Parent exists then return null, otherwise simultaneously attempt to contact to each existing Parent with the following steps. + If the failover function resolves to a `WindowProxy` object, repeat steps 1-3 & 5 above substituting the `WindowProxy` for the candidate window objects before proceeding to the next step. - i) Add a listener (`.addEventListener("message", (event) => {})`) to receive messages from each Parent. +### Step 2: Validate app & instance identity - ii) Send a "Handshake" message to each Parent (set for any origin since parent origin is unknown) with a unique nonce (one time random key). +Apps and instance of them must identify themselves so that DAs can positively associate them with their corresponding AppD records and any existing instance identity. - iii) Accept the first correct response received (discard all others). Correct responses MUST include the original nonce. Stop the timer when a correct response is received. If the response has transferred a MessagePort (see HTML Channel Messaging API) then proceed to completion of step 2 (below). If the response contains a `url` then proceed to sub-step iv. If neither is provided then reject with "ErrorOnConnect". +In the current FDC3 version, no identity validation procedures are provided for Desktop Agent Preload interfaces. Hence, it is the responsibility of such implementations to validate the identity of apps within their scope and to ensure that they update their own record of the identity if it changes during the life of the window hosting the application (i.e. due to a navigation event). It is expected that FDC3 will adopt identity validation procedures for Desktop Agent Preload interfaces in future. - iv) If a `url` has been provided, open a hidden iframe to the URL and listen on the `onload` event. Once the iframe is loaded, repeat steps i through iii substituting the hidden iframe for Parents. +#### 2.1 Determine App Identity - **Race Resolution** +In Desktop Agent Proxy interfaces, identity is ascertained via the [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message, which should be the first message sent on the `MessagePort` received by the `getAgent()` implementation after receiving it via [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json). Any other messages sent via the `MessagePort` prior to successful validation of a [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message should be ignored. - If a timeout wins the race then call the failover function (if one was provided to `getAgent()`). - - If the failover function... - - **(I)** ...resolves to a DesktopAgent then return it. +An app identity is determined and an `appId` assigned by matching the application to an AppD record already known to the Desktop Agent, based on the `identityUrl` provided in the [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message. An additional `actualUrl` field MUST also be provided to indicate whether the app overrode its `identityUrl` and to allow for logging. The origin (protocol, domain and port) of the `identityUrl`, `actualUrl` and the `MessageEvent.origin` field of the original `WCP1Hello` message that started the connection flow MUST all match. See the [Browser-Resident Desktop Agent Specification](./browserResidentDesktopAgents#validating-app-identity) for details of how to match an `identityUrl` to the `details.url` field of an AppD record. - **(II)** ...resolves to a WindowProxy then race a new timer and return to previous step i. +If the app identity is not recognized, the Desktop Agent MUST respond with a [WCP5ValidateAppIdentityFailedResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) message and stop handling further messages on the `MessagePort`. On receiving this message, the `getAgent()` implementation should reject with an Error object with the `AccessDenied` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. - **(III)** ...resolves to a URL then race a new timer and return to previous step iv. +If the app identity is recognized the Desktop Agent will assign the appropriate appId and move on to determining the instance identity. - **(IV)** ...resolves to anything else then return null. +#### 2.2 Determine Instance Identity - > If the _failover function itself_ results in a timeout then do not rerun the failover function. +If this instance of the application has connected to a Desktop Agent before and is reconnecting (due to a navigation or refresh event) then the optional `instanceId` and `instanceUuid` should be set in the [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message. The Desktop Agent MUST use these values to determine if it recognizes the app instance identity and that it was previously applied to application with the same `appId`. - **Completion of Step 2** +An `instanceUuid` is used to validate instance identity because `instanceId` of an application is available to other apps through the FDC3 API and might be used to 'spoof' an identity. On the other hand, `instanceUuid` is only issued through the WCP to the specific app instance and is used as a shared secret to enable identity validation. However, as `SessionStorage` data maybe cloned when new windows are opened via `window.open()`, Desktop Agents MUST also compare the `WindowProxy` object (via the `==` operator) that the original `WCP1Hello` messages were received on to determine if they represent the same window. - If neither MessagePort nor `DesktopAgent` has been obtained then reject the `getAgent()` promise with "AgentNotFound". - - If a MessagePort was obtained then create a `DesktopAgent` capable of using BCP to communicate with the remote DA (see below). +See the [Browser-Resident Desktop Agent Specification](./browserResidentDesktopAgents#validating-instance-identity) for further details of how instance identity is assigned. - Proceed to step 2. +If no existing instance identity (`instanceId` and `instanceUuid`) is provided, or instance identity validation fails (as the `instanceUuid` is not known, or either the `appId` or `WindowProxy` objects don't match the previous connection), then the Desktop Agent MUST assign new `instanceId` and `instanceUuid` values. -### Validate App Identity (step 2) +The Desktop Agent MUST then respond with a [WCP5ValidateAppIdentityResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing the assigned `appId`, `instanceId` and `instanceUuid` values and the [`ImplementationMetadata`](../ref/Metadata#implementationmetadata) Object for the Desktop Agent. This message indicates that the Destkop Agent will accept the application and we can begin processesing Desktop Agent Communication Protocol (DACP) messages relating to FDC3 API calls over the `MessagePort`. -Apps must identify themselves so that DAs can positively associate them with their corresponding AppD records. Identity is ascertained from either: +### Step 3: Persist DesktopAgentDetails to SessionStorage -(1) A fully qualified `appId` field (provided to `getAgent()`). -(2) An `appDUrl` field (provided to `getAgent()`) and then a subsequent http call (or internal lookup) to retrieve that AppD record. -(3) An `instanceUuid` field provided from SessionStorage (due to a navigation event). -The DA will validate the app's identity against the app window's origin (protocol, domain and port) by positively matching it with either the `details.url` or `details.allowedOrigins` fields in the AppD record referenced by either appId or appDUrl. To ensure the validity of this process, appD records and the application itself SHOULD be served via HTTPS. -1) If the DA is injected (using the `fdc3` global object - Preload DA) then: - Simply return the global object as the DesktopAgent. -2) If the DA is browser-resident, then: +See the [reference documentation for getAgent()](../ref/GetAgent#persisted-connection-data) for further details of the `DesktopAgentDetails` record that should be saved to SessionStorage. - Send a "WCPValidateAppIdentity" message to the DA via the MessagePort. Include the DesktopAgentDetails record if it exists in SessionStorage. - - The DA will validate the app's identity against the origin of the message. It will return either a "IdentityValidationSucceeded" or "IdentityValidationFailed" message. The `getAgent()` promise should reject on a failure message. -Success responses (from calls to `validateAppIdentity()` or "WCPValidateAppIdentityResponse" messages) MUST include `ImplementationMetadata` (describing the DA), `AppMetadata` (describing the instance), and `DesktopAgentDetails` record (to be persisted in the next step). +See Persisted Connection Data -### Persist DesktopAgentDetails to SessionStorage (step 3) Once a connection is established, the `DesktopAgentDetails` record that was returned by the DA MUST be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key. > Note - SessionStorage is ephemeral, only existing for the duration of the window's life. There is no concern with the key being overwritten or conflicting with other DAs. -### Resolve promise with DesktopAgent interface (step 4) +### Step 4: Resolve promise with DesktopAgent interface (step 4) Resolve the `getAgent()` promise with an object containing the `DesktopAgent` from step 1, and `ImplementationMetadata` and `AppMetadata` which were provided by the response from step 2. +## WCP Message Schemas and Types + ## Communicating using the Desktop Agent Communication Protocol (DACP) The `DesktopAgent` instantiated by calls to `getAgent()` uses the Desktop Agent Communication Protocol (DACP) to interact with the DA that is located in another frame. This protocol is based on exchanges of a standardized set of Flux Standard Actions (FSAs). The protocol is bi-directional. Every FSA that is transmitted results in a corresponding FSA in return, either containing requested data or simply acknowledging receipt. diff --git a/docs/context/ref/ChatRoom.md b/docs/context/ref/ChatRoom.md index 14a5924a5..fbd2d8c04 100644 --- a/docs/context/ref/ChatRoom.md +++ b/docs/context/ref/ChatRoom.md @@ -1,6 +1,7 @@ --- title: ChatRoom sidebar_label: ChatRoom + --- # ChatRoom diff --git a/schemas/api/common.schema.json b/schemas/api/common.schema.json index a8720aa32..bc7961689 100644 --- a/schemas/api/common.schema.json +++ b/schemas/api/common.schema.json @@ -8,7 +8,7 @@ "ConnectionAttemptUuid": { "title": "Connection Attempt UUID", "type": "string", - "description": "Unique identifier for a for an attempt to connect to a Desktop Agent" + "description": "Unique identifier for a for an attempt to connect to a Desktop Agent. A Unique UUID should be used in the first (WCP1Hello) message and should be quoted in all subsequent messages to link them to the same connection attempt." }, "RequestUuid": { "title": "Request UUID", diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 8bac2ecaf..3e8df9815 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -53,7 +53,7 @@ export type GetAgentType = ( * * @property {number} timeout Number of milliseconds to allow for an fdc3 * implementation to be found before calling the failover function or - * rejecting (default 1000). Note that the timeout is cancelled as soon as a + * 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. * From 29ba0a207b2970c4573f80830ddcdf76947cbd34 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 10 Sep 2024 13:39:28 +0100 Subject: [PATCH 087/152] deleting defunct protocol files --- src/protocols/bcp.ts | 517 ------------------------------------------- src/protocols/wcp.ts | 42 ---- 2 files changed, 559 deletions(-) delete mode 100644 src/protocols/bcp.ts delete mode 100644 src/protocols/wcp.ts diff --git a/src/protocols/bcp.ts b/src/protocols/bcp.ts deleted file mode 100644 index ed1104073..000000000 --- a/src/protocols/bcp.ts +++ /dev/null @@ -1,517 +0,0 @@ -/** - * Browser Communication Protocol (BCP) is used for communication between the "@finos/fdc3" library and Browser-Resident DAs. - */ -import { Context } from "../context/ContextTypes"; -import { ContextType } from "../context/ContextType"; -import { AppIdentifier } from "../api/AppIdentifier"; -import { AppMetadata } from "../api/AppMetadata"; -import { AppIntent } from "../api/AppIntent"; -import { ImplementationMetadata } from "../api/ImplementationMetadata"; -import { DisplayMetadata } from "../api/DisplayMetadata"; -import { ContextMetadata } from "../api/ContextMetadata"; - -type BCPMeta = { - messageId: string; -}; - -/** - * Messages initiated by DA (inbound) - */ - -export type BCPBroadcastInbound = { - type: "BroadcastInbound"; - payload: { - context: Context; - channel?: string; - }; - meta: BCPMeta; -}; - -// Sent from library to DA, and then also received by library from DA -export type BCPIntentResult = { - type: "raiseIntentResult"; - payload: { - context?: Context; - channel?: { - channelId: string; - type: "user" | "app" | "private"; - }; - // This will match the responseId from a previous BCPRaiseIntentResponse message - responseId: string; - }; - meta: BCPMeta; -}; - -// Increment subscriber count -export type BCPPrivateChannelOnAddContextListener = { - type: "privateChannelOnAddContextListener"; - payload: { - channelId: string; - contextType?: string; - }; - meta: BCPMeta; -}; - -export type BCPPrivateChannelOnDisconnect = { - type: "privateChannelOnDisconnect"; - payload: { - channelId: string; - }; - meta: BCPMeta; -}; - -// Decrement subscriber count -export type BCPPrivateChannelOnUnsubscribe = { - type: "privateChannelOnUnsubscribe"; - payload: { - channelId: string; - contextType?: string; - }; - meta: BCPMeta; -}; - -export type BCPRaiseIntentInbound = { - type: "raiseIntentInbound"; - payload: { - intent: string; - context: Context; - metadata ?: ContextMetadata; - responseId: string; // generated by DA - }; - meta: BCPMeta; -}; - -/** - * Messages responses from DA - */ - -export type BCPAck = { - type: "ack"; - payload: { - error?: string; - } - meta: BCPMeta; -} - -export type BCPCreatePrivateChannelResponse = { - type: "createPrivateChannelResponse"; - payload: { - channel : { - channelId: string; - type: "private"; - displayMetadata ?: DisplayMetadata; - } - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPFindInstancesResponse = { - type: "findInstancesResponse"; - payload: { - instances: Array; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPFindIntentResponse = { - type: "findIntentResponse"; - payload: { - appIntent?: AppIntent; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPFindIntentsByContextResponse = { - type: "findIntentsByContextResponse"; - payload: { - appIntents: Array; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetAppMetadataResponse = { - type: "getAppMetadataResponse"; - payload: { - appMetadata?: AppMetadata; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetCurrentChannelResponse = { - type: "getCurrentChannelResponse"; - payload: { - // Channel will be undefined if not on a current channel - channel?: { - id: string; - displayMetadata ?: DisplayMetadata; - } - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetCurrentContextResponse = { - type: "getCurrentContextResponse"; - payload: { - // context will be undefined if no current context - context?: Context; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetInfoResponse = { - type: "getInfoResponse"; - payload: { - implementationMetadata: ImplementationMetadata; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetOrCreateChannelResponse = { - type: "getOrCreateChannelResponse"; - payload: { - channel : { - channelId: string; - type: "user" | "app" | "private"; - displayMetadata ?: DisplayMetadata; - } - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPGetUserChannelsResponse = { - type: "getUserChannelsResponse"; - payload: { - channels: Array<{ - id: string; - displayMetadata ?: DisplayMetadata; - }>; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPOpenResponse = { - type: "openResponse"; - payload: { - appIdentifier: AppIdentifier; - } | { - error: string; - }; - meta: BCPMeta; -}; - -export type BCPRaiseIntentResponse = { - type: "raiseIntentResponse"; - payload: { - data: { - source: AppMetadata; - intent: string; - version?: string; - // Use this to match up future inbound BCPRaiseIntentResult messages - responseId: string; - }; - } | { - error: string; - }; - meta: BCPMeta; -}; - -/** - * Messages initiated by client (outbound) - */ - -export type BCPAddContextListener = { - type: "addContextListener"; - payload: { - contextType: ContextType; - // Set if the listener was added to a specific channel - channel?: string; - }; - meta: BCPMeta; -}; - -export type BCPAddIntentListener = { - type: "addIntentListener"; - payload: { - intent: string; - }; - meta: BCPMeta; -}; - -export type BCPBroadcast = { - type: "broadcast"; - payload: { - context: Context; - channelId?: string; - }; - meta: BCPMeta; -}; - -export type BCPCreatePrivateClient = { - type: "createPrivateClient"; - meta: BCPMeta; -}; - -export type BCPFindInstances = { - type: "findInstances"; - app: AppIdentifier; - meta: BCPMeta; -}; - -export type BCPFindIntent = { - type: "findIntent"; - payload: { - intent: string; - context?: Context; - resultType?: string; - }; - meta: BCPMeta; -}; - -export type BCPFindIntentsByContext = { - type: "findIntentsByContext"; - payload: { - context?: Context; - resultType?: string; - }; - meta: BCPMeta; -}; - -export type BCPGetAppMetadata = { - type: "getAppMetadata"; - payload: { - app: AppIdentifier; - } - meta: BCPMeta; -}; - -export type BCPGetCurrentChannel = { - type: "getCurrentChannel"; - meta: BCPMeta; -}; - -export type BCPGetCurrentContext = { - type: "getCurrentContext"; - payload: { - contextType?: string; - channelId?: string; - }; - meta: BCPMeta; -}; - -export type BCPGetInfo = { - type: "getInfo"; - meta: BCPMeta; -}; - -export type BCPGetOrCreateChannel = { - type: "getOrCreateChannel"; - payload: { - channelId: string - } - meta: BCPMeta; -}; - -export type BCPGetUserChannels = { - type: "getUserChannels"; - meta: BCPMeta; -}; - -export type BCPJoinUserChannel = { - type: "joinUserChannel"; - payload: { - channelId: string; - }; - meta: BCPMeta; -}; - -export type BCPLeaveCurrentChannel = { - type: "leaveCurrentChannel"; - meta: BCPMeta; -}; - -export type BCPOpen = { - type: "open"; - payload: { - app: AppIdentifier | string; - context?: Context; - }; - meta: BCPMeta; -}; - -export type BCPPrivateChannelDisconnect = { - type: "privateChannelDisconnect"; - payload: { - channelId: string; - }; - meta: BCPMeta; -}; - -export type BCPRaiseIntent = { - type: "raiseIntent"; - payload: { - intent: string | null; - context: Context; - app ?: AppIdentifier; - }; - meta: BCPMeta; -}; - -export type BCPRaiseIntentForContext = { - type: "raiseIntentForContext"; - payload: { - context?: Context; - app?: AppIdentifier - }; - meta: BCPMeta; -}; - -export type BCPRemoveContextListener = { - type: "removeContextListener"; - payload: { - contextType?: ContextType; - channel?: string; - }; - meta: BCPMeta; -}; - -export type BCPRemoveIntentListener = { - type: "removeIntentListener"; - payload: { - intent: string; - }; - meta: BCPMeta; -}; - -/** - * Library provided UI messages - */ - -// Sent from DA to App when an intent needs resolution and it does -// not have the ability to provide a graphical intent resolver -export type BCPResolveIntent = { - type: "resolveIntent", - payload: { - // The intent to resolve - intent: { - name: string; - displayName: string; - }; - - // If raiseIntentForContext - context?: Context; - - // The DA provided list of launchable apps which may resolve this intent type - launchableApps: { - meta: AppMetadata; - appId: string; - }[]; - - // The DA provided lists of open apps which are registered to receive this intent type - openApps: { - meta: AppMetadata; - instanceId ?: string; - }[]; - - // the originatingAppName may be displayed by the UI Resolver to indicate the source of the request. - originatingAppName?: string; - }; - meta: BCPMeta; -} - -// Response from App to DA with intent resolution -export type BCPResolveIntentResponse = { - type: "resolveIntentResponse", - payload: { - error?: string; - - // appId is set if intent should be delivered to a launchable app - appId?: string; - - // instanceId is set if intent should be delivered to open app - instanceId?: string; - }; - meta: BCPMeta; -} - -// Sent by the DA to the App to enable the built-in channel selector. `id` can optionally be set in order -// to initialize a specific channel (for instance when rehydrating an app from previous persistence) -export type BCPinitializeChannelSelector = { - type: "initializeChannelSelector", - payload: { - // The id of the channel which should be enabled, if one should be initially set - id?: string; - } -} - -export type BCPMessageInbound = - BCPBroadcastInbound | - BCPIntentResult | - BCPPrivateChannelOnAddContextListener | - BCPPrivateChannelOnDisconnect | - BCPPrivateChannelOnUnsubscribe | - BCPRaiseIntentInbound; - -export type BCPMessageResponse = - BCPAck | - BCPCreatePrivateChannelResponse | - BCPFindInstancesResponse | - BCPFindIntentResponse | - BCPFindIntentsByContextResponse | - BCPGetAppMetadataResponse | - BCPGetCurrentChannelResponse | - BCPGetCurrentContextResponse | - BCPGetInfoResponse | - BCPGetOrCreateChannelResponse | - BCPGetUserChannelsResponse | - BCPOpenResponse | - BCPRaiseIntentResponse; - -export type BCPMessageOutbound = - BCPAddContextListener | - BCPAddIntentListener | - BCPBroadcast | - BCPCreatePrivateClient | - BCPFindInstances | - BCPFindIntent | - BCPFindIntentsByContext | - BCPGetAppMetadata | - BCPGetCurrentChannel | - BCPGetCurrentContext | - BCPGetInfo | - BCPGetOrCreateChannel | - BCPGetUserChannels | - BCPJoinUserChannel | - BCPLeaveCurrentChannel | - BCPOpen | - BCPPrivateChannelDisconnect | - BCPRaiseIntent | - BCPRaiseIntentForContext | - BCPRemoveContextListener | - BCPRemoveIntentListener; - -export type BCPMessage = BCPMessageInbound | BCPMessageOutbound | BCPMessageResponse; - -// Returns a new type that equals the member of an original type -export type Member = T[K]; - -export type BCPType = Member; \ No newline at end of file diff --git a/src/protocols/wcp.ts b/src/protocols/wcp.ts deleted file mode 100644 index f3f0e8d07..000000000 --- a/src/protocols/wcp.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Web Connection Protocol (WCP) is used to establish connectivity between "@finos/fdc3" and Browser-Resident DAs. - */ - -import { AppMetadata } from "../api/AppMetadata"; -import { DesktopAgentDetails } from "../api/GetAgent"; -import { ImplementationMetadata } from "../api/ImplementationMetadata"; - -export type WCPHandshake = { - type: "handshake", - payload: { - nonce: string; - } -} - -// Note, the MessagePort is _transferred_ using postMessage. It is not included in the response packet. -export type WCPHandshakeResponse = { - type: "handshakeResponse", - payload: { - nonce: string; - // If set, then getAgent() will open a hidden iframe and restart negotiation with that frame - url?: string; - } -} - -export type WCPValidateAppIdentity = { - type: "validateAppIdentity", - payload: { - desktopAgentDetails?: DesktopAgentDetails; - } -} - -export type WCPValidateAppIdentityResponse = { - type: "validateAppIdentityResponse", - payload: { - desktopAgentDetails: DesktopAgentDetails; - appMetaData: AppMetadata; - implementationMetadata: ImplementationMetadata; - } | { - error: string; - } -} \ No newline at end of file From f9eeeb7073d5fff2f053c597fb8e80e7b177a7f2 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 10 Sep 2024 13:40:01 +0100 Subject: [PATCH 088/152] WIP Web Connection Protocol docs --- docs/api/specs/webConnectionProtocol.md | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index c6ecaa66b..2b9ff9fb5 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -58,7 +58,7 @@ Existing `DesktopAgentDetails` records MUST be used to limit discovery actions ( If use of the persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with `AgentNotFound` error from the [`AgentError`](../ref/Errors#agenterror) enumeration. -#### 1.2 Desktop Agent discovery +#### 1.2 Desktop Agent Discovery Next, attempt to discover whether Desktop Agent Preload or Desktop Agent Proxy interfaces are available, within a specified timeout. The optional `params.timeout` argument to `getAgent()` allows an application to specify the timeout that should be used, with a default value of 750ms. Discovery of Desktop Agent Preload or Desktop Agent Proxy interfaces should be conducted in parallel where possible and the timeout cancelled as soon as an interface is discovered. If a `DesktopAgentDetails` record was found in the previous step, limit discovery to the interface specified, or, if an `agentUrl` property was specified in the `DesktopAgentDetails` record, skip the discovery step entirely and proceed to the next step. @@ -202,42 +202,42 @@ See the [Browser-Resident Desktop Agent Specification](./browserResidentDesktopA If no existing instance identity (`instanceId` and `instanceUuid`) is provided, or instance identity validation fails (as the `instanceUuid` is not known, or either the `appId` or `WindowProxy` objects don't match the previous connection), then the Desktop Agent MUST assign new `instanceId` and `instanceUuid` values. -The Desktop Agent MUST then respond with a [WCP5ValidateAppIdentityResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing the assigned `appId`, `instanceId` and `instanceUuid` values and the [`ImplementationMetadata`](../ref/Metadata#implementationmetadata) Object for the Desktop Agent. This message indicates that the Destkop Agent will accept the application and we can begin processesing Desktop Agent Communication Protocol (DACP) messages relating to FDC3 API calls over the `MessagePort`. +The Desktop Agent MUST then respond with a [WCP5ValidateAppIdentityResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing the assigned `appId`, `instanceId` and `instanceUuid` values and the [`ImplementationMetadata`](../ref/Metadata#implementationmetadata) Object for the Desktop Agent. This message indicates that the Desktop Agent will accept the application and we can begin processing [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) messages relating to FDC3 API calls over the `MessagePort`. ### Step 3: Persist DesktopAgentDetails to SessionStorage +Once a connection is established, and the app and instance identity determined, a `DesktopAgentDetails` record must be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key by the `getAgent()` implementation. This record includes: - - - - +- The `identityUrl` and `actualUrl` passed to `getAgent()`. +- The `appId`, `instanceId`, and `instanceUuid` assigned by the DA. +- An `agentUrl` field with the URL provided in any [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message that was received + - Used to skip sub-steps 1-3 in the discovery process described in Step 1.2. + - If no [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message was received, omit this field. +- An `agentType` field which indicates what type of connection to the DA was established + - Used to limit the discovery process in Step 1.2 above to only allow agents of the same type. + - the `getAgent()` implementation should determine what value to set, from the `WebDesktopAgentType` enumeration for this field based on what happened during the discovery process. See the [reference documentation for getAgent()](../ref/GetAgent#persisted-connection-data) for further details of the `DesktopAgentDetails` record that should be saved to SessionStorage. +:::tip -See Persisted Connection Data - - -Once a connection is established, the `DesktopAgentDetails` record that was returned by the DA MUST be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key. +SessionStorage is ephemeral, only existing for the duration of the window's life. Hence, there is no concern with the key being overwritten or conflicting with other DAs. -> Note - SessionStorage is ephemeral, only existing for the duration of the window's life. There is no concern with the key being overwritten or conflicting with other DAs. +::: ### Step 4: Resolve promise with DesktopAgent interface (step 4) -Resolve the `getAgent()` promise with an object containing the `DesktopAgent` from step 1, and `ImplementationMetadata` and `AppMetadata` which were provided by the response from step 2. +Resolve the `getAgent()` promise with an object containing either a `DesktopAgent` implementation (that was found at `window.fdc3` or returned by a `failover` function) or a 'Desktop Agent Proxy' implementation (a class implementing the `DesktopAgent` interface that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent over the `MessagePort`). -## WCP Message Schemas and Types +## Providing Channel Selector and Intent Resolver UIs -## Communicating using the Desktop Agent Communication Protocol (DACP) +`getAgent()` MUST provide UI for channel selector and intent resolution. These can be OPTIONALLY utilized by the DA. The DA will send the `BCPInitializeChannelSelector` message if it does not have its own way to manage channel selection. It will send `BCPResolveIntent` if it does not have a way to present an intent resolver UI. -The `DesktopAgent` instantiated by calls to `getAgent()` uses the Desktop Agent Communication Protocol (DACP) to interact with the DA that is located in another frame. This protocol is based on exchanges of a standardized set of Flux Standard Actions (FSAs). The protocol is bi-directional. Every FSA that is transmitted results in a corresponding FSA in return, either containing requested data or simply acknowledging receipt. +`getAgent()` SHOULD implement UI within the app's DOM, for instance with an overlay for channel selector and a modal dialog for intent resolution. -BCP uses the HTML Channel Messaging API, communicating via the `MessagePort` object that was established by the Web Connection Protocol (WCP) covered above. -See [Desktop Agent Communication Protocol ](./desktopAgentCommunicationProtocol .md) +## WCP Message Schemas and Types + -## Built in UI (Channel Selector and Intent Resolver) -`getAgent()` MUST provide UI for channel selector and intent resolution. These can be OPTIONALLY utilized by the DA. The DA will send the `BCPInitializeChannelSelector` message if it does not have its own way to manage channel selection. It will send `BCPResolveIntent` if it does not have a way to present an intent resolver UI. -`getAgent()` SHOULD implement UI within the app's DOM, for instance with an overlay for channel selector and a modal dialog for intent resolution. From acf8064ca8f03a660ff2832859ae967d2e515713 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 10:20:34 +0100 Subject: [PATCH 089/152] adding raiseIntentRequestUuid reference field to intentEvent and intentResultRequest --- schemas/api/intentEvent.schema.json | 7 ++++++- schemas/api/intentResultRequest.schema.json | 7 ++++++- src/api/BrowserTypes.ts | 21 +++++++++++++++++---- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/schemas/api/intentEvent.schema.json b/schemas/api/intentEvent.schema.json index a4d2b87cf..f6523b8cf 100644 --- a/schemas/api/intentEvent.schema.json +++ b/schemas/api/intentEvent.schema.json @@ -45,11 +45,16 @@ "title": "Originating AppIdentifier", "description": "Details of the application instance that raised the intent", "$ref": "api.schema.json#/definitions/AppIdentifier" + }, + "raiseIntentRequestUuid": { + "title": "raiseIntentRequest UUID", + "type": "string", + "description": "The requestUuid value of the raiseIntentRequest that the intentEvent being sent relates to." } }, "additionalProperties": false, "required": [ - "intent", "context" + "intent", "context", "raiseIntentRequestUuid" ] } } diff --git a/schemas/api/intentResultRequest.schema.json b/schemas/api/intentResultRequest.schema.json index c83e6004e..d95de797f 100644 --- a/schemas/api/intentResultRequest.schema.json +++ b/schemas/api/intentResultRequest.schema.json @@ -36,12 +36,17 @@ "type": "string", "description": "The eventUuid value of the intentEvent that the result being sent relates to." }, + "raiseIntentRequestUuid": { + "title": "raiseIntentRequest UUID", + "type": "string", + "description": "The requestUuid value of the raiseIntentRequest that the result being sent relates to." + }, "intentResult": { "$ref": "api.schema.json#/definitions/IntentResult" } }, "required": [ - "intentResult" + "intentEventUuid", "raiseIntentRequestUuid", "intentResult" ], "additionalProperties": false } diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 03ebc2804..c2ca5d285 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1319,7 +1319,9 @@ export interface Image { * * Unique identifier for an event message sent from a Desktop Agent to an app. * - * Unique identifier for a for an attempt to connect to a Desktop Agent + * Unique identifier for a for an attempt to connect to a Desktop Agent. A Unique UUID + * should be used in the first (WCP1Hello) message and should be quoted in all subsequent + * messages to link them to the same connection attempt. * * Should be set if the raiseIntent request returned an error. */ @@ -2544,6 +2546,11 @@ export interface IntentEventPayload { * Details of the application instance that raised the intent */ originatingApp?: AppIdentifier; + /** + * The requestUuid value of the raiseIntentRequest that the intentEvent being sent relates + * to. + */ + raiseIntentRequestUuid: string; } /** @@ -2643,8 +2650,12 @@ export interface IntentResultRequestPayload { /** * The eventUuid value of the intentEvent that the result being sent relates to. */ - intentEventUuid?: string; - intentResult: IntentResult; + intentEventUuid: string; + intentResult: IntentResult; + /** + * The requestUuid value of the raiseIntentRequest that the result being sent relates to. + */ + raiseIntentRequestUuid: string; } export interface IntentResult { @@ -5196,6 +5207,7 @@ const typeMap: any = { { json: "context", js: "context", typ: r("Context") }, { json: "intent", js: "intent", typ: "" }, { json: "originatingApp", js: "originatingApp", typ: u(undefined, r("AppIdentifier")) }, + { json: "raiseIntentRequestUuid", js: "raiseIntentRequestUuid", typ: "" }, ], false), "IntentListenerUnsubscribeRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, @@ -5216,8 +5228,9 @@ const typeMap: any = { { json: "type", js: "type", typ: r("IntentResultRequestType") }, ], false), "IntentResultRequestPayload": o([ - { json: "intentEventUuid", js: "intentEventUuid", typ: u(undefined, "") }, + { json: "intentEventUuid", js: "intentEventUuid", typ: "" }, { json: "intentResult", js: "intentResult", typ: r("IntentResult") }, + { json: "raiseIntentRequestUuid", js: "raiseIntentRequestUuid", typ: "" }, ], false), "IntentResult": o([ { json: "context", js: "context", typ: u(undefined, r("Context")) }, From 391c923c88c5d7b02837f43f5bb5a393c266a4a7 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 12:07:14 +0100 Subject: [PATCH 090/152] Move AgentError into Errors.ts --- .../api/specs/browserResidentDesktopAgents.md | 2 +- src/api/Errors.ts | 23 ++++++++++++++++++ src/api/GetAgent.ts | 24 ------------------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 70623e7f2..aa6abe3f6 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -204,7 +204,7 @@ Checking whether an application has closed may be achieved by a number of approa - By checking the `closed` property of WindowProxy objects that were received via the `source` property of the original `WCP1Hello` message, or any subsequent message over the `MessageChannel`. `closed` will be true if the window or frame was closed or destroyed, or the window or frame has navigated cross-domain. - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. Hence, checking the `closed` property will not catch all cases. - If an equivalent `WindowProxy` object (`WindowProxy` objects can be compared with `==` and will be equivalent if they represent the same window) is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. -- By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation SHOULD attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. +- By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation MUST attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. - By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. diff --git a/src/api/Errors.ts b/src/api/Errors.ts index 2b10d5944..cf5d26b8c 100644 --- a/src/api/Errors.ts +++ b/src/api/Errors.ts @@ -3,6 +3,29 @@ * Copyright FINOS FDC3 contributors - see NOTICE file */ +/** + * Contains constants representing the errors that can be encountered when trying to connect to a web-based Desktop Agent with the getAgent function. + */ +export enum AgentError { + /** Returned if no Desktop Agent was found by any means available or + * if the Agent previously connected to is not contactable on a + * subsequent connection attempt.*/ + AgentNotFound = "AgentNotFound", + + /** Returned if validation of the app identity by the Desktop Agent + * Failed or the app is not being allowed to connect to the Desktop Agent + * for another reason. */ + AccessDenied = "AccessDenied", + + /** Returned if an error or exception occurs while trying to set + * up communication with a Desktop Agent. */ + ErrorOnConnect = "ErrorOnConnect", + + /** Returned if either the failover function itself, or what it returned, + * was not the right type. */ + InvalidFailover = "InvalidFailover" +}; + /** Constants representing the errors that can be encountered when calling the `open` method on the DesktopAgent object (`fdc3`). */ export enum OpenError { /** Returned if the specified application is not found.*/ diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 3e8df9815..740ec7acb 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -92,30 +92,6 @@ type GetAgentParams = { failover?: (args: GetAgentParams) => Promise }; -/** - * Contains constants representing the errors that can be encountered when - * trying to connect to a web-based Desktop Agent with the getAgent function. - */ -export enum AgentError { - /** Returned if no Desktop Agent was found by any means available or - * if the Agent previously connected to is not contactable on a - * subsequent connection attempt.*/ - AgentNotFound = "AgentNotFound", - - /** Returned if validation of the app identity by the Desktop Agent - * Failed or the app is not being allowed to connect to the Desktop Agent - * for another reason. */ - AccessDenied = "AccessDenied", - - /** Returned if an error or exception occurs while trying to set - * up communication with a Desktop Agent. */ - ErrorOnConnect = "ErrorOnConnect", - - /** Returned if either the failover function itself, or what it returned, - * was not the right type. */ - InvalidFailover = "InvalidFailover" -}; - /** Type representing data on the Desktop Agent that an app * connected to that is persisted by the getAgent function * to be used when re-connecting (after a navigation or From 8bf29721bb7561295286f673679683a118f61f95 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 13:53:36 +0100 Subject: [PATCH 091/152] Near complete WCP docs --- docs/api/ref/GetAgent.md | 2 +- docs/api/specs/webConnectionProtocol.md | 109 ++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 80e3675a4..3f292990b 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -47,7 +47,7 @@ If no Desktop Agent is found, a failover function may be supplied by app allowin The definition of the `getAgent()` function is as follows: ```ts -type getAgent = ( +type GetAgentType = ( params?: GetAgentParams, ) => Promise; ``` diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 2b9ff9fb5..9225a1de9 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -10,18 +10,18 @@ The FDC3 Web Connection Protocol (WCP) is an experimental feature added to FDC3 ::: -The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the `@finos/fdc3` library, which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. :::tip -The @finos/fdc3 npm module provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. +The [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3) provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. ::: The WCP supports both interfaces to web-based Desktop Agents defined in the FDC3 Standard: - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. -- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the Desktop Agent Communication protocol to communicate with a Desktop Agent implementation running in another frame or window, via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). +- **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent implementation running in another frame or window, via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). The WCP allows FDC3-enabled application to detect which FDC3 web-interface is present and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). @@ -39,12 +39,13 @@ Further details for implementing Preload Desktop Agents (which use a Desktop Age ## Establishing Connectivity Using the Web Connection Protocol (WCP) -The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps which must be completed within the window of the web application trying to connect. Each step may contain sub-steps. +The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps which must be completed within the window of the web application trying to connect, followed by an optional disconnection step. Each step may contain sub-steps. 1. Locate a Desktop Agent interface 2. Validate app & instance identity 3. Persist connection details to SessionStorage 4. Return DesktopAgent interface +5. Disconnect ### Step 1: Locate a Desktop Agent interface @@ -229,15 +230,109 @@ SessionStorage is ephemeral, only existing for the duration of the window's life Resolve the `getAgent()` promise with an object containing either a `DesktopAgent` implementation (that was found at `window.fdc3` or returned by a `failover` function) or a 'Desktop Agent Proxy' implementation (a class implementing the `DesktopAgent` interface that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent over the `MessagePort`). +### Step 5: Disconnection + +Desktop Agent Preload interfaces, as used in container-based Desktop Agent implementations, are usually able to track the lifecycle and current URL of windows that host web apps in their scope. Hence, this is currently no requirement nor means for an app to indicate that it is closing, rather it is the responsibility of the Desktop Agent to update its internal state when an app closes or changes identity. + +However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. + +As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection must also be used, these are described in the [Disconnects section of the Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects). + +### getAgent Workflow Defined by WCP + +The workflow defined in the Web Connection protocol for `getAgent()` is summarized in the below diagram: + +```mermaid +--- +title: "Web Connection Protocol Flowchart" +--- +flowchart TD + A1([DesktopAgent launches app]) --> A2["`App calls **getAgent()**`"] + A2 --> A3["`Check for **DesktopAgentDetails** in **SessionStorage**`"] + A3 --> P1{"`Does **window.fdc3** exist?`"} + P1 ---|yes|P2["`stop **timeout**`"] + P2 --> P21["`Save **DesktopAgentDetails** to **SessionStorage**`"] + P21 -->P22(["`resolve with **window.fdc3**`"]) + P1 ---|No|P3["`Listen for **fdc3Ready**`"] + P3 --> P31["`**fdc3Ready event fires**`"] + P31 --> P32["`stop **timeout**`"] + P32 --> P33["`Save **DesktopAgentDetails** to **SessionStorage**`"] + P33 -->P34(["`resolve with **window.fdc3**`"]) + + A3 --> B1{"`Do **window.opener**, **window.parent**, **window.parent.opener** etc. exist?`"} + B1 --> B11["`Send **WCP1Hello** to all candidates`"] + B11 --> B2["`Receive **WCP2LoadUrl**`"] + B2 --> B21["`stop **timeout**`"] + B21 --> B22["`Create hidden iframe with URL`"] + B22 --> B23["`Send **WCP1Hello** to iframe`"] + B23 --> B3["`Receive **WCP3Handshake** with **MessagePort**`"] + B3 --> B31["`stop **timeout**`"] + B11 --> B3 + B31 --> B32["`Send **WCP4ValidateIdentity** on **MessagePort**`"] + B32 --> B321["`Receive **WCP5ValidateIdentityResponse**`"] + B321 --> B3211["`Create **DesktopAgentProxy** to process DACP messages`"] + B3211 --> B3212["`Save **DesktopAgentDetails** to **SessionStorage**`"] + B3212 --> B3213(["`resolve with **DesktopAgentProxy**`"]) + B32 --> B322["`Receive **WCP5ValidateIdentityFailedResponse**`"] + B322 --> B3221(["`reject with **AgentError.AccessDenied**`"]) + + A3 --> T1["`Set **timeout**`"] + T1 --> T2["`**timeout** expires`"] + T2 --> T3{"`Was a **failover** fn provided`"} + T3 ---|yes|T31["`Run failover`"] + T31 --> T311{"`Check failover return type`"} + T311 ---|**DesktopAgent**|T3111["`Save **DesktopAgentDetails** to **SessionStorage**`"] + T3111 --> T3112(["`resolve with **DesktopAgent**`"]) + T311 ---|**WindowProxy**|T3112["`Send **WCP1Hello** via **WindowProxy**`"] + T3112 --> B3 + T3 ---|no|T32(["`reject with **AgentError.AgentNotFound**`"]) +``` + ## Providing Channel Selector and Intent Resolver UIs -`getAgent()` MUST provide UI for channel selector and intent resolution. These can be OPTIONALLY utilized by the DA. The DA will send the `BCPInitializeChannelSelector` message if it does not have its own way to manage channel selection. It will send `BCPResolveIntent` if it does not have a way to present an intent resolver UI. +Users of FDC3 Desktop Agents often need access to UI controls that allow them to select user channels or to resolve intents that have multiple resolution options. Whilst apps can implement these UIs on their own via data and API calls provided by the `DesktopAgent` API, Desktop Agents typically provide these interfaces themselves. -`getAgent()` SHOULD implement UI within the app's DOM, for instance with an overlay for channel selector and a modal dialog for intent resolution. +However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. +The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via: + +- **[WCP1Hello](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json)**: Sent by an application and incorporating boolean `payload.intentResolver` and `payload.channelSelector` fields, which are set to false if either UI is not needed (defaults to true). +- **[WCP3Handshake](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json)**: Response sent by the Desktop Agent and incorporating `payload.intentResolverUrl` and `payload.channelSelectorUrl` fields, which should be set to the URL for each UI implementation, which should be loaded into an iframe to provide the UI (defaults to URLs for reference UI implementations provided by the FDC3 project). + +When UI iframes are created, the user interfaces may use messages incorporated into the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with the `getAgent()` implementation and through it the Desktop Agent. -## WCP Message Schemas and Types +//TODO complete description once finalized. + +```mermaid +flowchart LR + subgraph DA [Desktop Agent Window] + A[(Desktop Agent)] + end + A-->B["getAgent()"] + subgraph App [App Window] + B + subgraph iframe1 [iframe 1] + cs[Channel Selector] + end + subgraph iframe2 [iframe 2] + ir[Intent Resolver] + end + end + B-->cs + B-->ir +``` + +## WCP Message Schemas and Types +The WCP is used to implement the `getAgent()` function provided by the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3). Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. +The messages defined by the Web Connection Protocol are: +- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) +- [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) +- [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) +- [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) +- [`WCP5ValidateAppIdentityFailedResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) +- [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) +- [`WCP6Goodbye`](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) \ No newline at end of file From c66088ae8c9f08ea0c964f50f8db1849bdaa9da8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 14:03:52 +0100 Subject: [PATCH 092/152] Adjusting WCP diagram rendering --- docs/api/specs/webConnectionProtocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 9225a1de9..36c7182dc 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -259,7 +259,7 @@ flowchart TD P32 --> P33["`Save **DesktopAgentDetails** to **SessionStorage**`"] P33 -->P34(["`resolve with **window.fdc3**`"]) - A3 --> B1{"`Do **window.opener**, **window.parent**, **window.parent.opener** etc. exist?`"} + A3 --> B1{"`Do aprent refs exist?`"} B1 --> B11["`Send **WCP1Hello** to all candidates`"] B11 --> B2["`Receive **WCP2LoadUrl**`"] B2 --> B21["`stop **timeout**`"] From a501d99fb3764902bff7a4d7a025f230d231fe4f Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 15:19:06 +0100 Subject: [PATCH 093/152] Improvements to WCP protocol diagram --- docs/api/specs/webConnectionProtocol.md | 68 +++++++++++++------------ 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 36c7182dc..434b4d171 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -249,43 +249,47 @@ title: "Web Connection Protocol Flowchart" flowchart TD A1([DesktopAgent launches app]) --> A2["`App calls **getAgent()**`"] A2 --> A3["`Check for **DesktopAgentDetails** in **SessionStorage**`"] - A3 --> P1{"`Does **window.fdc3** exist?`"} - P1 ---|yes|P2["`stop **timeout**`"] - P2 --> P21["`Save **DesktopAgentDetails** to **SessionStorage**`"] + + subgraph "getAgent() imeplementation" + A3 --> P1{"`Does **window.fdc3** exist?`"} + P1 ---|yes|P2["`stop **timeout**`"] + P2 --> P21["`Save **DesktopAgentDetails** to **SessionStorage**`"] + P1 ---|No|P3["`Listen for **fdc3Ready**`"] + P3 --> P31["`**fdc3Ready event fires**`"] + P31 --> P32["`stop **timeout**`"] + P32 --> P33["`Save **DesktopAgentDetails** to **SessionStorage**`"] + + A3 --> B1{"`Do parent refs exist?`"} + B1 --> B11["`Send **WCP1Hello** to all candidates`"] + B11 --> B2["`Receive **WCP2LoadUrl**`"] + B2 --> B21["`stop **timeout**`"] + B21 --> B22["`Create hidden iframe with URL`"] + B22 --> B23["`Send **WCP1Hello** to iframe`"] + B23 --> B3["`Receive **WCP3Handshake** with **MessagePort**`"] + B3 --> B31["`stop **timeout**`"] + B11 --> B3 + B31 --> B32["`Send **WCP4ValidateIdentity** on **MessagePort**`"] + B32 --> B321["`Receive **WCP5ValidateIdentityResponse**`"] + B321 --> B3211["`Create **DesktopAgentProxy** to process DACP messages`"] + B3211 --> B3212["`Save **DesktopAgentDetails** to **SessionStorage**`"] + B32 --> B322["`Receive **WCP5ValidateIdentityFailedResponse**`"] + + A3 --> T1["`Set **timeout**`"] + T1 --> T2["`**timeout** expires`"] + T2 --> T3{"`Was a **failover** fn provided`"} + T3 ---|yes|T31["`Run failover`"] + T31 --> T311{"`Check failover return type`"} + T311 ---|**WindowProxy**|T3111["`Send **WCP1Hello** via **WindowProxy**`"] + T311 ---|**DesktopAgent**|T3112["`Save **DesktopAgentDetails** to **SessionStorage**`"] + T3111 --> B3 + end P21 -->P22(["`resolve with **window.fdc3**`"]) - P1 ---|No|P3["`Listen for **fdc3Ready**`"] - P3 --> P31["`**fdc3Ready event fires**`"] - P31 --> P32["`stop **timeout**`"] - P32 --> P33["`Save **DesktopAgentDetails** to **SessionStorage**`"] P33 -->P34(["`resolve with **window.fdc3**`"]) - - A3 --> B1{"`Do aprent refs exist?`"} - B1 --> B11["`Send **WCP1Hello** to all candidates`"] - B11 --> B2["`Receive **WCP2LoadUrl**`"] - B2 --> B21["`stop **timeout**`"] - B21 --> B22["`Create hidden iframe with URL`"] - B22 --> B23["`Send **WCP1Hello** to iframe`"] - B23 --> B3["`Receive **WCP3Handshake** with **MessagePort**`"] - B3 --> B31["`stop **timeout**`"] - B11 --> B3 - B31 --> B32["`Send **WCP4ValidateIdentity** on **MessagePort**`"] - B32 --> B321["`Receive **WCP5ValidateIdentityResponse**`"] - B321 --> B3211["`Create **DesktopAgentProxy** to process DACP messages`"] - B3211 --> B3212["`Save **DesktopAgentDetails** to **SessionStorage**`"] B3212 --> B3213(["`resolve with **DesktopAgentProxy**`"]) - B32 --> B322["`Receive **WCP5ValidateIdentityFailedResponse**`"] B322 --> B3221(["`reject with **AgentError.AccessDenied**`"]) - - A3 --> T1["`Set **timeout**`"] - T1 --> T2["`**timeout** expires`"] - T2 --> T3{"`Was a **failover** fn provided`"} - T3 ---|yes|T31["`Run failover`"] - T31 --> T311{"`Check failover return type`"} - T311 ---|**DesktopAgent**|T3111["`Save **DesktopAgentDetails** to **SessionStorage**`"] - T3111 --> T3112(["`resolve with **DesktopAgent**`"]) - T311 ---|**WindowProxy**|T3112["`Send **WCP1Hello** via **WindowProxy**`"] - T3112 --> B3 + T3112 --> T31121(["`resolve with **DesktopAgent**`"]) T3 ---|no|T32(["`reject with **AgentError.AgentNotFound**`"]) + T311 ---|**Other**|T3113["`reject with **AgentError.ErrorOnConnect**`"] ``` ## Providing Channel Selector and Intent Resolver UIs From 707601bbaf2a550a50674c15043058a0e48cd3e7 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 15:26:01 +0100 Subject: [PATCH 094/152] resuffling sections for better reading in WCP spec --- docs/api/specs/webConnectionProtocol.md | 34 ++++++++++++------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 434b4d171..7eb2536f7 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -10,7 +10,7 @@ The FDC3 Web Connection Protocol (WCP) is an experimental feature added to FDC3 ::: -The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. :::tip @@ -27,7 +27,7 @@ The WCP allows FDC3-enabled application to detect which FDC3 web-interface is pr :::tip -See the FDC3 [Glossary](../../fdc3-glossary) and [References](../../references.md) pages for definitions of terms and links to external APIs used throughout this document. +See the FDC3 [Glossary](../../fdc3-glossary) and [References](../../references.md) pages for definitions of terms and links to external APIs used throughout this document. ::: @@ -37,6 +37,18 @@ Further details for implementing Preload Desktop Agents (which use a Desktop Age ::: +## WCP Message Schemas and Types + +There are a number of message formats defined as part of the Web Connection Protocol, which will be referenced later in this document, these are: + +- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) +- [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) +- [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) +- [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) +- [`WCP5ValidateAppIdentityFailedResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) +- [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) +- [`WCP6Goodbye`](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) + ## Establishing Connectivity Using the Web Connection Protocol (WCP) The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps which must be completed within the window of the web application trying to connect, followed by an optional disconnection step. Each step may contain sub-steps. @@ -238,7 +250,7 @@ However, Browser Resident Desktop Agents working with a Desktop Agent Proxy inte As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection must also be used, these are described in the [Disconnects section of the Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects). -### getAgent Workflow Defined by WCP +### `getAgent()` Workflow Diagram The workflow defined in the Web Connection protocol for `getAgent()` is summarized in the below diagram: @@ -289,7 +301,7 @@ flowchart TD B322 --> B3221(["`reject with **AgentError.AccessDenied**`"]) T3112 --> T31121(["`resolve with **DesktopAgent**`"]) T3 ---|no|T32(["`reject with **AgentError.AgentNotFound**`"]) - T311 ---|**Other**|T3113["`reject with **AgentError.ErrorOnConnect**`"] + T311 ---|**Other**|T3113["`reject with **AgentError.InvalidFailover**`"] ``` ## Providing Channel Selector and Intent Resolver UIs @@ -326,17 +338,3 @@ flowchart LR B-->cs B-->ir ``` - -## WCP Message Schemas and Types - -The WCP is used to implement the `getAgent()` function provided by the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3). Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. - -The messages defined by the Web Connection Protocol are: - -- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) -- [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) -- [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) -- [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) -- [`WCP5ValidateAppIdentityFailedResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) -- [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) -- [`WCP6Goodbye`](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) \ No newline at end of file From efc7e7cb9a06548441ff539083523177869d41fc Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 15:37:42 +0100 Subject: [PATCH 095/152] fix rendering of WCP diagram in docusaurus --- docs/api/specs/webConnectionProtocol.md | 72 ++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 7eb2536f7..f32c7f36b 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -259,49 +259,49 @@ The workflow defined in the Web Connection protocol for `getAgent()` is summariz title: "Web Connection Protocol Flowchart" --- flowchart TD - A1([DesktopAgent launches app]) --> A2["`App calls **getAgent()**`"] - A2 --> A3["`Check for **DesktopAgentDetails** in **SessionStorage**`"] + A1([DesktopAgent launches app]) --> A2["App calls **getAgent()**"] + A2 --> A3["Check for **DesktopAgentDetails** in **SessionStorage**"] subgraph "getAgent() imeplementation" - A3 --> P1{"`Does **window.fdc3** exist?`"} - P1 ---|yes|P2["`stop **timeout**`"] - P2 --> P21["`Save **DesktopAgentDetails** to **SessionStorage**`"] - P1 ---|No|P3["`Listen for **fdc3Ready**`"] - P3 --> P31["`**fdc3Ready event fires**`"] - P31 --> P32["`stop **timeout**`"] - P32 --> P33["`Save **DesktopAgentDetails** to **SessionStorage**`"] + A3 --> P1{"Does **window.fdc3** exist?"} + P1 ---|yes|P2["stop **timeout**"] + P2 --> P21["Save **DesktopAgentDetails** to **SessionStorage**"] + P1 ---|No|P3["Listen for **fdc3Ready**"] + P3 --> P31["**fdc3Ready event fires**"] + P31 --> P32["stop **timeout**"] + P32 --> P33["Save **DesktopAgentDetails** to **SessionStorage**"] - A3 --> B1{"`Do parent refs exist?`"} - B1 --> B11["`Send **WCP1Hello** to all candidates`"] - B11 --> B2["`Receive **WCP2LoadUrl**`"] - B2 --> B21["`stop **timeout**`"] - B21 --> B22["`Create hidden iframe with URL`"] - B22 --> B23["`Send **WCP1Hello** to iframe`"] - B23 --> B3["`Receive **WCP3Handshake** with **MessagePort**`"] - B3 --> B31["`stop **timeout**`"] + A3 --> B1{"Do parent refs exist?"} + B1 --> B11["Send **WCP1Hello** to all candidates"] + B11 --> B2["Receive **WCP2LoadUrl**"] + B2 --> B21["stop **timeout**"] + B21 --> B22["Create hidden iframe with URL"] + B22 --> B23["Send **WCP1Hello** to iframe"] + B23 --> B3["Receive **WCP3Handshake** with **MessagePort**"] + B3 --> B31["stop **timeout**"] B11 --> B3 - B31 --> B32["`Send **WCP4ValidateIdentity** on **MessagePort**`"] - B32 --> B321["`Receive **WCP5ValidateIdentityResponse**`"] - B321 --> B3211["`Create **DesktopAgentProxy** to process DACP messages`"] - B3211 --> B3212["`Save **DesktopAgentDetails** to **SessionStorage**`"] - B32 --> B322["`Receive **WCP5ValidateIdentityFailedResponse**`"] + B31 --> B32["Send **WCP4ValidateIdentity** on **MessagePort**"] + B32 --> B321["Receive **WCP5ValidateIdentityResponse**"] + B321 --> B3211["Create **DesktopAgentProxy** to process DACP messages"] + B3211 --> B3212["Save **DesktopAgentDetails** to **SessionStorage**"] + B32 --> B322["Receive **WCP5ValidateIdentityFailedResponse**"] - A3 --> T1["`Set **timeout**`"] - T1 --> T2["`**timeout** expires`"] - T2 --> T3{"`Was a **failover** fn provided`"} - T3 ---|yes|T31["`Run failover`"] - T31 --> T311{"`Check failover return type`"} - T311 ---|**WindowProxy**|T3111["`Send **WCP1Hello** via **WindowProxy**`"] - T311 ---|**DesktopAgent**|T3112["`Save **DesktopAgentDetails** to **SessionStorage**`"] + A3 --> T1["Set **timeout**"] + T1 --> T2["**timeout** expires"] + T2 --> T3{"Was a **failover** fn provided"} + T3 ---|yes|T31["Run failover"] + T31 --> T311{"Check failover return type"} + T311 ---|**WindowProxy**|T3111["Send **WCP1Hello** via **WindowProxy**"] + T311 ---|**DesktopAgent**|T3112["Save **DesktopAgentDetails** to **SessionStorage**"] T3111 --> B3 end - P21 -->P22(["`resolve with **window.fdc3**`"]) - P33 -->P34(["`resolve with **window.fdc3**`"]) - B3212 --> B3213(["`resolve with **DesktopAgentProxy**`"]) - B322 --> B3221(["`reject with **AgentError.AccessDenied**`"]) - T3112 --> T31121(["`resolve with **DesktopAgent**`"]) - T3 ---|no|T32(["`reject with **AgentError.AgentNotFound**`"]) - T311 ---|**Other**|T3113["`reject with **AgentError.InvalidFailover**`"] + P21 -->P22(["resolve with **window.fdc3**"]) + P33 -->P34(["resolve with **window.fdc3**"]) + B3212 --> B3213(["resolve with **DesktopAgentProxy**"]) + B322 --> B3221(["reject with **AgentError.AccessDenied**"]) + T3112 --> T31121(["resolve with **DesktopAgent**"]) + T3 ---|no|T32(["reject with **AgentError.AgentNotFound**"]) + T311 ---|**Other**|T3113["reject with **AgentError.InvalidFailover**"] ``` ## Providing Channel Selector and Intent Resolver UIs From e18792dac0818d6419ea65264ddaa36e415aa04c Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 16:58:32 +0100 Subject: [PATCH 096/152] All null listeners types in addEventListenerRequest --- schemas/api/addEventListenerRequest.schema.json | 11 +++++++++-- src/api/BrowserTypes.ts | 8 +++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/schemas/api/addEventListenerRequest.schema.json b/schemas/api/addEventListenerRequest.schema.json index 9a05aeef2..1417499d0 100644 --- a/schemas/api/addEventListenerRequest.schema.json +++ b/schemas/api/addEventListenerRequest.schema.json @@ -33,8 +33,15 @@ "properties": { "type": { "title": "Event type", - "description": "The type of the event to be listened to.", - "$ref": "api.schema.json#/definitions/FDC3EventType" + "description": "The type of the event to be listened to or `null` to listen to all event types.", + "oneOf": [ + { + "$ref": "api.schema.json#/definitions/FDC3EventType" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false, diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index c2ca5d285..5bed871de 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -315,8 +315,6 @@ export interface FDC3Event { /** * The type of a (non-context and non-intent) event that may be received via the FDC3 API's * addEventListener function. - * - * The type of the event to be listened to. */ /** @@ -350,9 +348,9 @@ export interface AddEventListenerRequest { */ export interface AddEventListenerRequestPayload { /** - * The type of the event to be listened to. + * The type of the event to be listened to or `null` to listen to all event types. */ - type: "USER_CHANNEL_CHANGED"; + type: "USER_CHANNEL_CHANGED" | null; } /** @@ -4737,7 +4735,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("AddEventListenerRequestType") }, ], false), "AddEventListenerRequestPayload": o([ - { json: "type", js: "type", typ: r("FDC3EventType") }, + { json: "type", js: "type", typ: u(r("FDC3EventType"), null) }, ], false), "AddEventListenerResponse": o([ { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, From 3d373e5fbc9c60beee560501d45f44e50ea9bb87 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 11 Sep 2024 18:28:46 +0100 Subject: [PATCH 097/152] Introduction to and skeleton of DACP reference doc --- .../desktopAgentCommunicationProtocol.md | 92 ++++++++++--------- docs/api/specs/webConnectionProtocol.md | 6 +- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 2be68ab96..2a00ea242 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -12,75 +12,79 @@ The Desktop Agent Communication Protocol (DACP) is an experimental feature added ::: -DACP constitutes a set of messages that are used by the `@finos/fdc3` library to communicate with Browser-Resident DAs. Each message takes the form of a Flux Standard Action (FSA). Communications are bidirectional and occur over HTML standard MessagePorts. All messages are query/response. Responses may contain requested data or may simply be acknowledgement of receipt. - -:::note - -We refer to "the library" to mean the code imported from `@finos/fdc3` and initiated from a call to `getAgent()`. - -::: - -Type definitions for all DACP messages can be found here: [bcp.ts](TODO). +The Desktop Agent Communication Protocol (DACP) constitutes a set of standardized JSON messages or 'wire protocol' that can be used to implement an interface to a Desktop Agent, encompassing all API calls events defined in the [Desktop Agent API](../ref/DesktopAgent.md). For example, the DACP is used by the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3) to communicate with Browser-Resident Desktop Agents or a connection setup via the [FDC3 Web Connection Protocol](./webConnectionProtocol). ## Protocol conventions -The protocol is divided into groups of messages: - -1) Messages sent from the library to the DA. These typically have a 1:1 correspondence with function calls on `DesktopAgent` and `Channel`, and ancillary functionality such as unsubscribing. +DACP messages are defined in [JSON Schema](https://json-schema.org/) in the [FDC3 github repository](https://github.com/finos/FDC3/tree/fdc3-for-web/schemas/api). -2) Response messages, sent from the DA to the library. Every message sent from the library to the DA will receive a response. In most cases, the type will simply have "Response" appended. For instance, the response message for `getInfo` is `getInfoResponse`. For all other cases the `DACPAck` message will be the response. Every response's payload will contain an error string if an error occurred, otherwise it will contain the expected data. +:::tip -3) Asynchronous "inbound" messages, sent from the DA to the library. These messages are due to actions in other apps, such as an inbound context resulting from another app's broadcast. These messages have the name of the originating message appended with `Inbound`. For example, if another app called `broadcast` then this app would receive a message called `broadcastInbound`. +TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): -Every message has a `meta.messageId`. Initiating messages must set this to be a unique string. Response messages must use the string from their corresponding initiating message. The `messageId` can be used by library and DA to match incoming message responses with their initial requests. +```ts +import {BrowserTypes} from '@finos.fdc3'; +``` -## Multiplexing - -For any given contextType or intent, the library should only ever send `DACPAddContextListener` or `DACPAddIntentListener` one time. The DA is only responsible for sending any given Context or Intent _once_ to an app. The DA may ignore duplicate listener registrations. - -If the app has registered multiple listeners for these types then it is the responsibility of the _library_ to multiplex the delivered Context, or to choose a specific intent listener. - -When the API calls the unsubscriber for a listener then `DACPRemoveContextListener` or `DACPRemoveIntentListener` should be sent to the DA. - -## Intents +::: -Refer [Private Channel examples](../ref/PrivateChannel.md#server-side-example) to understand how intent transactions work. +The protocol is composed of several different classes of message, each governed by a message schema: -When an app ("client") calls `raiseIntent()` or `raiseIntentByContext()`, the library MUST send the corresponding `DACPRaiseIntent` or `DACPRaiseIntentByContext` message to the DA. +1) **App Request Messages** ([schema](https://fdc3.finos.org/schemas/next/api/appRequest.schema.json)): + - Messages sent by an application representing an API call, such as [`DesktopAgent.broadcast`](../ref/DesktopAgent#broadcast), [`Channel.addContextListener`](../ref/Channel#addcontextlistener), or [`Listener.unsubscribe`](../ref/Types#listener). + - Message names all end in 'Request'. + - Each instance of a request message sent is uniquely identified by a `meta.requestUuid` field. -The DA will resolve the intent and then deliver a `DACPIntentInbound` message to the library in the resolved app ("server"). This message will contain a `responseId` that has been generated by the DA. +2) **Agent Response Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json)): + - Response messages sent from the DA to the application, each relating to a corresponding _App Request Message_. + - Message names all end in 'Response'. + - Each instance of an Agent Response Message is uniquely identified by a `meta.responseUuid` field. + - Each instance of an Agent Response Message quotes the `meta.requestUuid` value of the message it is responding to. -After the message has been sent, the DA will respond back to the "client" app's library with a `DACPRaiseIntentResponse` message containing that `responseId`. +3) **Agent Event Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json)): + - Messages sent from the DA to the application that are due to actions in other applications, such as an inbound context resulting from another app's broadcast. + - Message names all end in 'Event'. + - Each instance of an Agent Response Message is uniquely identified by a `meta.eventUuid` field. -If the "client" app then calls `getResult()`, then the library will wait until a `DACPIntentResult` message is received with a corresponding `responseId`. It will resolve the `getResult()` call with either a Context or PrivateChannel depending on the contents of the result. +Each individual message is also governed by a message schema, which is composed with the schema for the message type. -Meanwhile, if the "server" app's intent handler resolves to a Channel or Context then the library should send a `DACPIntentResult` with the `responseId` that was initially received from `DACPIntentInbound`. +:::info +In rare cases, the payload of a request or event message may quote the `requestUuid` or `eventUuid` of another message that it represents a response to, e.g. `intentResultRequest` quotes the `eventUuid` of the `intentEvent` that delivered the intent and context to the app, as well as the `requestUuid` of the `raiseIntentRequest` message that originally raised the intent. -## Intent Resolver +::: -The DA should send `DACPResolveIntent` if it requires an external UI for intent resolution. This MUST include the list of available apps which are capable of being launched to handle the intent, and it MUST include the list of open apps which are capable of handling the intent. The "@finos/fdc3" library will present UI to the end user, and then will respond with a `DACPResolveIntentResponse` containing the user's choice. +All messages defined in the DACP follow a common structure: -DAs are free to provide their own intent resolution UIs if they have this capability. +```json +{ + "type": "string", // string identifying the message type + "payload": { + //message payload fields defined for each message type + }, + "meta": { + "timestamp": "2024-09-17T10:15:39+00:00" + //other meta fields determined by each 'class' of message + // these include requestUuid, responseUuid and eventUuid + // and a source field identifying an app where appropriate + } +} +``` -## Channels +## Registering Listeners and Multiplexing -The DA should send `DACPInitializeChannelSelector` if it requires the app to provide UI for channel selection. The "@finos/fdc3" library will provide the UI when this message is received. +//messages are sent to register and unregister listeners so that DAs can send only relevant messages to app for security -Any message related to a channel contains a `channelId` field. It is the responsibility of each party (DA and library) to correlate `channelId` fields with the correct local objects. +//DAs only need to send one event message, even when there are multiple listeners, getAgent() should fire all relevant listeners. -For instance, when the library receives a `DACPBroadcastInbound` message, it should look for the `channelId` field, and only deliver that message to listeners on the corresponding local `Channel` object. +## Message Definitions -Likewise, when an app calls `channel.broadcast()` then the library should send the `DACPBroadcast` message with the `channelId` set accordingly. +### Desktop Agent and Channel API -## Private Channels +### PrivateChannels -In general, private channels behave as channels. The DA MUST assign a unique `channelId` in response to `DACPCreatePrivateChannel` messages. `DACPBroadcast` and `DACPAddContextListener` messages can be transmitted with this `channelId`. +### User Interfaces -See [Intents](#intents) for the process that is used to established a private channel. -FDC3's `PrivateChannel` object has some specific functions, each of which has a corresponding DACP message. For instance, `PrivateChannel.onAddContextListener()` can be implemented using the `DACPPrivateChannelOnAddContextListener` message. Each of these types of messages contains a `channelId` which can be used to identify the channel. -The DA should send `DACPPrivateChannelOnAddContextListener` and `DACPPrivateChannelOnUnsubscribe` messages whenever `DACPAddContextListener` or `DACPRemoveContextListener` is called on a private channel. These will be delivered to the library regardless of whether a client has actually called `onAddContextListener()` and `onUnsubscribe()`. It is the library's responsibility to track these calls and either deliver or discard the messages accordingly. -Likewise, the DA should send `DACPPrivateChannelOnDisconnect` whenever the `DACPPrivateChannelDisconnect` message is received. It is the library's responsibility to deliver or discard this message. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index f32c7f36b..2dc00d1b4 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -10,11 +10,11 @@ The FDC3 Web Connection Protocol (WCP) is an experimental feature added to FDC3 ::: -The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. :::tip -The [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3) provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. +The [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3) provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. ::: @@ -308,7 +308,7 @@ flowchart TD Users of FDC3 Desktop Agents often need access to UI controls that allow them to select user channels or to resolve intents that have multiple resolution options. Whilst apps can implement these UIs on their own via data and API calls provided by the `DesktopAgent` API, Desktop Agents typically provide these interfaces themselves. -However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [@finos/fdc3 npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. +However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via: From e9153df777b4220c555369c4f862eb18c690acc8 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 12 Sep 2024 12:25:29 +0100 Subject: [PATCH 098/152] Adjustments to WCP flow diagram and adding some implementation tips --- docs/api/specs/webConnectionProtocol.md | 81 ++++++++++++++----------- website/docusaurus.config.js | 18 ++++++ 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 2dc00d1b4..51ec2fb11 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -161,7 +161,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp ``` Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. - 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. + 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. If no response is received from any candidate, the `getAgent()` implementation MAY retry sending the `WCP1Hello` message periodically until the timeout is reached. 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: * Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: ```ts @@ -177,7 +177,10 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp return ifrm.contentWindow; } ``` - * Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. + * Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. + :::tip + To ensure that the iframe is ready to receive the `WCP1Hello` message when the `load` event fires, implementations should call `window.addEventListener` for the `message` event synchronously and as early as possible. + ::: 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have be received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. Add a listener (`port.addEventListener("message", (event) => {})`) to receive messages from the selected `candidate`, before moving on to the next stage. @@ -188,6 +191,9 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp If the failover function resolves to a `DesktopAgent` implementation it should be immediately returned in the same way as a `window.fdc3` reference was and handled as if it represents a Desktop Agent Preload interface. If the failover function resolves to a `WindowProxy` object, repeat steps 1-3 & 5 above substituting the `WindowProxy` for the candidate window objects before proceeding to the next step. + :::tip + Where possible, iframe failover functions should wait for the iframe or window represented by a `WindowProxy` object to be ready to receive messages before resolving. For an iframe this is a case of waiting for the `load` event to fire. + ::: ### Step 2: Validate app & instance identity @@ -259,49 +265,50 @@ The workflow defined in the Web Connection protocol for `getAgent()` is summariz title: "Web Connection Protocol Flowchart" --- flowchart TD - A1([DesktopAgent launches app]) --> A2["App calls **getAgent()**"] - A2 --> A3["Check for **DesktopAgentDetails** in **SessionStorage**"] - - subgraph "getAgent() imeplementation" - A3 --> P1{"Does **window.fdc3** exist?"} - P1 ---|yes|P2["stop **timeout**"] - P2 --> P21["Save **DesktopAgentDetails** to **SessionStorage**"] - P1 ---|No|P3["Listen for **fdc3Ready**"] - P3 --> P31["**fdc3Ready event fires**"] - P31 --> P32["stop **timeout**"] - P32 --> P33["Save **DesktopAgentDetails** to **SessionStorage**"] + A1([DesktopAgent launches app]) --> A2["App calls getAgent()"] + A2 --> A3["Check for DesktopAgentDetails in SessionStorage"] + + subgraph "getAgent() implementation" + A3 --> P1{"Does window.fdc3 exist?"} + P1 -->|yes|P2["stop timeout"] + P2 --> P21["Save DesktopAgentDetails to SessionStorage"] + P1 -->|No|P3["Listen for fdc3Ready"] + P3 --> P31["fdc3Ready event fires"] + P31 --> P32["stop timeout"] + P32 --> P33["Save DesktopAgentDetails to SessionStorage"] A3 --> B1{"Do parent refs exist?"} - B1 --> B11["Send **WCP1Hello** to all candidates"] - B11 --> B2["Receive **WCP2LoadUrl**"] - B2 --> B21["stop **timeout**"] + B1 -->|yes|B11["Send WCP1Hello to all candidates"] + B11 --> B2["Receive WCP2LoadUrl"] + B2 --> B21["stop timeout"] B21 --> B22["Create hidden iframe with URL"] - B22 --> B23["Send **WCP1Hello** to iframe"] - B23 --> B3["Receive **WCP3Handshake** with **MessagePort**"] - B3 --> B31["stop **timeout**"] + B22 --> B23["await iframe's load event"] + B23 --> B24["Send WCP1Hello to iframe"] + B24 --> B3["Receive WCP3Handshake with MessagePort"] + B3 --> B31["stop timeout"] B11 --> B3 - B31 --> B32["Send **WCP4ValidateIdentity** on **MessagePort**"] - B32 --> B321["Receive **WCP5ValidateIdentityResponse**"] - B321 --> B3211["Create **DesktopAgentProxy** to process DACP messages"] - B3211 --> B3212["Save **DesktopAgentDetails** to **SessionStorage**"] - B32 --> B322["Receive **WCP5ValidateIdentityFailedResponse**"] + B31 --> B32["Send WCP4ValidateIdentity on MessagePort"] + B32 --> B321["Receive WCP5ValidateIdentityResponse"] + B321 --> B3211["Create DesktopAgentProxy to process DACP messages"] + B3211 --> B3212["Save DesktopAgentDetails to SessionStorage"] + B32 --> B322["Receive WCP5ValidateIdentityFailedResponse"] - A3 --> T1["Set **timeout**"] - T1 --> T2["**timeout** expires"] - T2 --> T3{"Was a **failover** fn provided"} - T3 ---|yes|T31["Run failover"] + A3 --> T1["Set timeout"] + T1 --> T2["timeout expires"] + T2 --> T3{"Was a failover fn provided"} + T3 -->|yes|T31["Run failover"] T31 --> T311{"Check failover return type"} - T311 ---|**WindowProxy**|T3111["Send **WCP1Hello** via **WindowProxy**"] - T311 ---|**DesktopAgent**|T3112["Save **DesktopAgentDetails** to **SessionStorage**"] + T311 -->|WindowProxy|T3111["Send WCP1Hello via WindowProxy"] + T311 -->|DesktopAgent|T3112["Save DesktopAgentDetails to SessionStorage"] T3111 --> B3 end - P21 -->P22(["resolve with **window.fdc3**"]) - P33 -->P34(["resolve with **window.fdc3**"]) - B3212 --> B3213(["resolve with **DesktopAgentProxy**"]) - B322 --> B3221(["reject with **AgentError.AccessDenied**"]) - T3112 --> T31121(["resolve with **DesktopAgent**"]) - T3 ---|no|T32(["reject with **AgentError.AgentNotFound**"]) - T311 ---|**Other**|T3113["reject with **AgentError.InvalidFailover**"] + P21 -->P22(["resolve with window.fdc3"]) + P33 -->P34(["resolve with window.fdc3"]) + B3212 --> B3213(["resolve with DesktopAgentProxy"]) + B322 --> B3221(["reject with AgentError.AccessDenied"]) + T3112 --> T31121(["resolve with DesktopAgent"]) + T3 -->|no|T32(["reject with AgentError.AgentNotFound"]) + T311 -->|other|T3113["reject with AgentError.InvalidFailover"] ``` ## Providing Channel Selector and Intent Resolver UIs diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index bbaf5d1c8..4c06d33cf 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -210,6 +210,24 @@ module.exports={ "width": 50, "href": "https://finos.org" } + }, + "mermaid": { + "options": { + "markdownAutoWrap": true, + "wrap": true, + "flowchart": { + "titleTopMargin": 30, + "subGraphTitleMargin": { + "top": 30, + "bottom": 30 + }, + "nodeSpacing": 30, + "rankSpacing": 50, + "wrappingWidth": 60, + "diagramPadding": 5, + "useMaxWidth": true + } + } } } } From 2d28bb83d62ebedd91c34768a70e03b458c86ce5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 12 Sep 2024 12:49:04 +0100 Subject: [PATCH 099/152] more minor WCP flow diagram tweaks --- docs/api/specs/webConnectionProtocol.md | 8 ++++---- website/docusaurus.config.js | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 51ec2fb11..041b3d2f6 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -264,12 +264,12 @@ The workflow defined in the Web Connection protocol for `getAgent()` is summariz --- title: "Web Connection Protocol Flowchart" --- -flowchart TD +flowchart TB A1([DesktopAgent launches app]) --> A2["App calls getAgent()"] - A2 --> A3["Check for DesktopAgentDetails in SessionStorage"] + A2 --> A3 - subgraph "getAgent() implementation" - A3 --> P1{"Does window.fdc3 exist?"} + subgraph getAgent ["getAgent()"] + A3["Check for DesktopAgentDetails in SessionStorage"] --> P1{"Does window.fdc3 exist?"} P1 -->|yes|P2["stop timeout"] P2 --> P21["Save DesktopAgentDetails to SessionStorage"] P1 -->|No|P3["Listen for fdc3Ready"] diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 4c06d33cf..d616052db 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -213,8 +213,10 @@ module.exports={ }, "mermaid": { "options": { + "htmlLabels": false, "markdownAutoWrap": true, "wrap": true, + "wrappingWidth": 50, "flowchart": { "titleTopMargin": 30, "subGraphTitleMargin": { @@ -223,9 +225,10 @@ module.exports={ }, "nodeSpacing": 30, "rankSpacing": 50, - "wrappingWidth": 60, "diagramPadding": 5, - "useMaxWidth": true + "useMaxWidth": true, + "htmlLabels": false, + "wrappingWidth": 50 } } } From 7ae1421629af507d10f33304fb14c0321ac9dad2 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 12 Sep 2024 12:53:37 +0100 Subject: [PATCH 100/152] Making all CSS properties in iframeHello optional As requested by @robmoffat However, it is unclear what the default values would then be - meaning it is defined the defaults only in the `getAgent()` implementation and hence variable. --- schemas/api/iFrameHello.schema.json | 2 +- src/api/BrowserTypes.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index d3658f00b..d29c86418 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -45,7 +45,7 @@ "maxHeight": {"type": "string", "title": "maxHeight", "description": "The maximum height to apply to the iframe"}, "maxWidth": {"type": "string", "title": "maxWidth", "description": "The maximum with to apply to the iframe"} }, - "required": ["height", "width", "top", "left"] + "required": [] } }, "additionalProperties": false, diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 5bed871de..1a9819296 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -2288,11 +2288,11 @@ export interface InitialCSS { /** * The initial height of the iframe */ - height: string; + height?: string; /** * The initial left property to apply to the iframe */ - left: string; + left?: string; /** * The maximum height to apply to the iframe */ @@ -2308,7 +2308,7 @@ export interface InitialCSS { /** * The initial top property to apply to the iframe */ - top: string; + top?: string; /** * The transition property to apply to the iframe */ @@ -2316,7 +2316,7 @@ export interface InitialCSS { /** * The initial width of the iframe */ - width: string; + width?: string; /** * The initial zindex to apply to the iframe */ @@ -5146,14 +5146,14 @@ const typeMap: any = { ], false), "InitialCSS": o([ { json: "bottom", js: "bottom", typ: u(undefined, "") }, - { json: "height", js: "height", typ: "" }, - { json: "left", js: "left", typ: "" }, + { json: "height", js: "height", typ: u(undefined, "") }, + { json: "left", js: "left", typ: u(undefined, "") }, { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, { json: "right", js: "right", typ: u(undefined, "") }, - { json: "top", js: "top", typ: "" }, + { json: "top", js: "top", typ: u(undefined, "") }, { json: "transition", js: "transition", typ: u(undefined, "") }, - { json: "width", js: "width", typ: "" }, + { json: "width", js: "width", typ: u(undefined, "") }, { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, ], "any"), "IframeMessage": o([ From c60dcfa48f536021ac787b96fe698dcddff06e28 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 12 Sep 2024 18:25:06 +0100 Subject: [PATCH 101/152] Removing `addEventListenerEvent` as it was superseded by `ChannelChangedEvent` some time ago --- schemas/api/addEventListenerEvent.schema.json | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 schemas/api/addEventListenerEvent.schema.json diff --git a/schemas/api/addEventListenerEvent.schema.json b/schemas/api/addEventListenerEvent.schema.json deleted file mode 100644 index 5807b54c8..000000000 --- a/schemas/api/addEventListenerEvent.schema.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/addEventListenerEvent.schema.json", - "type": "object", - "title": "addEventListener Event", - "description": "An event message from the Desktop Agent to an app for a specified event type.", - "allOf": [ - { - "$ref": "agentEvent.schema.json" - }, - { - "type": "object", - "properties": { - "type": { - "$ref": "#/$defs/AddEventListenerEventType" - }, - "payload": { - "$ref": "#/$defs/AddEventListenerEventPayload" - }, - "meta": true - }, - "additionalProperties": false - } - ], - "$defs": { - "AddEventListenerEventType": { - "title": "AddEventListener Event Message Type", - "const": "addEventListenerEvent" - }, - "AddEventListenerEventPayload": { - "title": "addEventListener Event Payload", - "type": "object", - "properties": { - "event": { - "$ref": "api.schema.json#/definitions/FDC3Event" - } - }, - "additionalProperties": false, - "required": [ - "event" - ] - } - } -} \ No newline at end of file From 6d864a79c6c0ae0fef6cccbc973fc9ac7d56779a Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 12 Sep 2024 18:25:40 +0100 Subject: [PATCH 102/152] improving WCP schema intro and WIP draft of DACP docs --- .../desktopAgentCommunicationProtocol.md | 290 +++++++++++++++++- docs/api/specs/webConnectionProtocol.md | 35 ++- website/docusaurus.config.js | 4 +- 3 files changed, 317 insertions(+), 12 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 2a00ea242..a89886343 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -30,18 +30,18 @@ import {BrowserTypes} from '@finos.fdc3'; The protocol is composed of several different classes of message, each governed by a message schema: -1) **App Request Messages** ([schema](https://fdc3.finos.org/schemas/next/api/appRequest.schema.json)): +1. **App Request Messages** ([schema](https://fdc3.finos.org/schemas/next/api/appRequest.schema.json)): - Messages sent by an application representing an API call, such as [`DesktopAgent.broadcast`](../ref/DesktopAgent#broadcast), [`Channel.addContextListener`](../ref/Channel#addcontextlistener), or [`Listener.unsubscribe`](../ref/Types#listener). - Message names all end in 'Request'. - Each instance of a request message sent is uniquely identified by a `meta.requestUuid` field. -2) **Agent Response Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json)): +2. **Agent Response Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json)): - Response messages sent from the DA to the application, each relating to a corresponding _App Request Message_. - Message names all end in 'Response'. - Each instance of an Agent Response Message is uniquely identified by a `meta.responseUuid` field. - Each instance of an Agent Response Message quotes the `meta.requestUuid` value of the message it is responding to. -3) **Agent Event Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json)): +3. **Agent Event Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json)): - Messages sent from the DA to the application that are due to actions in other applications, such as an inbound context resulting from another app's broadcast. - Message names all end in 'Event'. - Each instance of an Agent Response Message is uniquely identified by a `meta.eventUuid` field. @@ -71,20 +71,294 @@ All messages defined in the DACP follow a common structure: } ``` -## Registering Listeners and Multiplexing +`meta.timestamp` fields are formatted as strings, according to the format defined by [ISO 8601-1:2019](https://www.iso.org/standard/70907.html), which is produced in JavaScript via the `Date` class's `toISOString()` function, e.g. `(new Date()).toISOString()`. + +### Routing, Registering Listeners & Multiplexing + +//messages SHOULD be routed rather than implement over a bus //messages are sent to register and unregister listeners so that DAs can send only relevant messages to app for security //DAs only need to send one event message, even when there are multiple listeners, getAgent() should fire all relevant listeners. -## Message Definitions +## Message Definitions Supporting FDC3 API calls + +This section provides details of the messages defined in the DACP, grouped according to the FDC3 API functions that they support, and defined by JSON Schema files. Many of these message definitions make use of JSON versions of [metadata](../ref/Metadata) and other [types](../ref/Types) defined by the Desktop Agent API, the JSON versions of which can be found in [api.schema.json](https://fdc3.finos.org/schemas/next/api/api.schema.json), while a number of DACP specific object definitions that are reused through the messages can be found in [common.schema.json](https://fdc3.finos.org/schemas/next/api/common.schema.json) + +### `DesktopAgent` + +#### `addContextListener()` + +Request and response used to implement the API call: +- [addContextListenerRequest](https://fdc3.finos.org/schemas/next/api/addContextListenerRequest.schema.json) +- [addContextListenerResponse](https://fdc3.finos.org/schemas/next/api/addContextListenerResponse.schema.json) + +Event message used to context objects that have been broadcast: +- [broadcastEvent](https://fdc3.finos.org/schemas/next/api/broadcastEvent.schema.json) + +Message exchange for removing the context listener: +- [contextListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeRequest.schema.json) +- [contextListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeResponse.schema.json) + +#### `addEventListener()` + +Request and response used to implement the API call: +- [addEventListenerRequest](https://fdc3.finos.org/schemas/next/api/addEventListenerRequest.schema.json) +- [addEventListenerResponse](https://fdc3.finos.org/schemas/next/api/addEventListenerResponse.schema.json) + +Event messages used to deliver events that have occurred: + +- [channelChangedEvent](https://fdc3.finos.org/schemas/next/api/channelChangedEvent.schema.json) + +Message exchange for removing the event listener: + +- [eventListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeRequest.schema.json) +- [eventListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeResponse.schema.json) + +#### `addIntentListener()` + +Request and response used to implement the API call: + +- [addIntentListenerRequest](https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json) +- [addIntentListenerResponse](https://fdc3.finos.org/schemas/next/api/addIntentListenerResponse.schema.json) + +Event message used to a raised intent and context object from another app to the listener: + +- [intentEvent](https://fdc3.finos.org/schemas/next/api/intentEvent.schema.json) + +An additional request and response used to deliver a result from the intent handler to the Desktop Agent, so that it can convey it back to the raising application: + +- [intentResultRequest](https://fdc3.finos.org/schemas/next/api/intentResultRequest.schema.json) +- [intentResultResponse](https://fdc3.finos.org/schemas/next/api/intentResultResponse.schema.json) + +Message exchange for removing the intent listener: + +- [intentListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeRequest.schema.json) +- [intentListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json) + + + + +```mermaid +sequenceDiagram + AppA ->> DesktopAgent: raiseIntentRequest + DesktopAgent ->> AppB: intentEvent + DesktopAgent ->> AppA: raiseIntentResponse +``` + +```mermaid +sequenceDiagram + AppA ->> DesktopAgent: raiseIntentRequest + DesktopAgent ->> AppB: intentEvent + DesktopAgent ->> AppA: raiseIntentResponse + break when AppIntent return with multiple options + DesktopAgent-->AppA: getAgent displays IntentResolver + AppA-->DesktopAgent: User picks an option + end + AppA ->> DesktopAgent: raiseIntentRequest + Note left of AppA: New request includes specific `app` target
/and new requestUuid + DesktopAgent ->> AppB: intentEvent + DesktopAgent ->> AppA: raiseIntentResponse + AppB ->> DesktopAgent: intentResultRequest + DesktopAgent ->> AppB: intentResultResponse + DesktopAgent ->> AppA: raiseIntentResultResponse +``` + +```mermaid +sequenceDiagram + AppA ->> DesktopAgent: raiseIntentRequest + DesktopAgent ->> AppB: intentEvent + break DA determines there are multiple options + DesktopAgent-->AppA: Desktop Agent displays IntentResolver + AppA-->DesktopAgent: User picks an option + end + DesktopAgent ->> AppA: raiseIntentResponse + AppB ->> DesktopAgent: intentResultRequest + DesktopAgent ->> AppB: intentResultResponse + DesktopAgent ->> AppA: raiseIntentResultResponse +``` + +#### `broadcast()` + +Request and response used to implement the API call: + +- [broadcastRequest](https://fdc3.finos.org/schemas/next/api/broadcastRequest.schema.json) +- [broadcastResponse](https://fdc3.finos.org/schemas/next/api/broadcastResponse.schema.json) + +See [`addContextListener()`](#addcontextlistener) above for the `broadcastEvent` used to deliver the broadcast to other apps. + +#### `createPrivateChannel()` + +Request and response used to implement the API call: + +- [createPrivateChannelRequest](https://fdc3.finos.org/schemas/next/api/createPrivateChannelRequest.schema.json) +- [createPrivateChannelResponse](https://fdc3.finos.org/schemas/next/api/createPrivateChannelResponse.schema.json) + +#### `findInstances()` + +Request and response used to implement the API call: + +- [findInstancesRequest](https://fdc3.finos.org/schemas/next/api/findInstancesRequest.schema.json) +- [findInstancesResponse](https://fdc3.finos.org/schemas/next/api/findInstancesResponse.schema.json) + +#### `findIntent()` + +Request and response used to implement the API call: + +- [findIntentRequest](https://fdc3.finos.org/schemas/next/api/findIntentRequest.schema.json) +- [findIntentResponse](https://fdc3.finos.org/schemas/next/api/findIntentResponse.schema.json) + +#### `findIntentsByContext()` + +Request and response used to implement the API call: + +- [findIntentsByContextRequest](https://fdc3.finos.org/schemas/next/api/findIntentsByContextRequest.schema.json) +- [findIntentsByContextResponse](https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json) + +#### `getAppMetadata()` + +Request and response used to implement the API call: + +- [getAppMetadataRequest](https://fdc3.finos.org/schemas/next/api/getAppMetadataRequest.schema.json) +- [getAppMetadataResponse](https://fdc3.finos.org/schemas/next/api/getAppMetadataResponse.schema.json) + +#### `getCurrentChannel()` + +Request and response used to implement the API call: + +- [getCurrentChannelRequest](https://fdc3.finos.org/schemas/next/api/getCurrentChannelRequest.schema.json) +- [getCurrentChannelResponse](https://fdc3.finos.org/schemas/next/api/getCurrentChannelResponse.schema.json) + +#### `getInfo()` + +Request and response used to implement the API call: + +- [getInfoRequest](https://fdc3.finos.org/schemas/next/api/getInfoRequest.schema.json) +- [getInfoResponse](https://fdc3.finos.org/schemas/next/api/getInfoResponse.schema.json) + +#### `getOrCreateChannel()` + +Request and response used to implement the API call: + +- [getOrCreateChannelRequest](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelRequest.schema.json) +- [getOrCreateChannelResponse](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelResponse.schema.json) + +#### `getUserChannels()` + +Request and response used to implement the API call: + +- [getUserChannelsRequest](https://fdc3.finos.org/schemas/next/api/getUserChannelsRequest.schema.json) +- [getUserChannelsResponse](https://fdc3.finos.org/schemas/next/api/getUserChannelsResponse.schema.json) + +#### `joinUserChannel()` + +Request and response used to implement the API call: + +- [joinUserChannelRequest](https://fdc3.finos.org/schemas/next/api/joinUserChannelRequest.schema.json) +- [joinUserChannelResponse](https://fdc3.finos.org/schemas/next/api/joinUserChannelResponse.schema.json) + +#### `leaveCurrentChannel()` + +Request and response used to implement the API call: + +- [leaveCurrentChannelRequest](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelRequest.schema.json) +- [leaveCurrentChannelResponse](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelResponse.schema.json) + +#### `open()` + +Request and response used to implement the API call: + +- [openRequest](https://fdc3.finos.org/schemas/next/api/openRequest.schema.json) +- [openResponse](https://fdc3.finos.org/schemas/next/api/openResponse.schema.json) + +Where a context object is passed (e.g. `fdc3.open(app, context)`) the `broadcastEvent` message described above in [`addContextListener`](#addcontextlistener) should be used to deliver it after the context listener has been added: + +```mermaid +sequenceDiagram + AppA ->> DesktopAgent: openRequest
(with context) + break Desktop Agent spawns AppB + AppB-->DesktopAgent: AppB connects to DesktopAgent + end + AppB ->> DesktopAgent: addContextListenerRequest + DesktopAgent ->> AppB: addContextListenerResponse + DesktopAgent ->> AppB: broadcastEvent +``` + +#### `raiseIntent()` + +Request and response used to implement the API call: + +- [raiseIntentRequest](https://fdc3.finos.org/schemas/next/api/raiseIntentRequest.schema.json) +- [raiseIntentResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentResponse.schema.json) +- [raiseIntentResultResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json) + +#### `raiseIntentForContext()` + +Request and response used to implement the API call: + +- [raiseIntentForContextRequest](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextRequest.schema.json) +- [raiseIntentForContextResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextResponse.schema.json) + +### `Channel` + +Owing to the significant overlap between the FDC3 `DesktopAgent` and `Channel` API, which includes the ability to retrieve and work with User channels as App Channels, most of the messaging for the `Channel` API is shared with `DesktopAgent`. Specifically, all messages defined in the the [`broadcast`](#broadcast) and [`addContextListener`](#addcontextlistener) sections above are reused, with a few minor differences to note: + +- When working with a specific channel, the `channelId` property in `addContextListenerRequest` should be set to the ID of the channel, where it is set to `null` to work with the current user channel. +- When receiving a `broadcastEvent` a `channelId` that is `null` indicates that the context was sent via a call to `fdc3.open` and does not relate to a channel. + +The following additional function is unique to the `Channel` interface: + +#### `getCurrentContext()` + +Request and response used to implement the API call: + +- [getCurrentContextRequest](https://fdc3.finos.org/schemas/next/api/getCurrentContextRequest.schema.json) +- [getCurrentContextResponse](https://fdc3.finos.org/schemas/next/api/getCurrentContextResponse.schema.json) + +### `PrivateChannel` + +The `PrivateChannel` interface extends `Channel` with a number of additional functions that are supported by the following messages: + +#### `addEventListener()` + +Request and response used to implement the API call: + +- [privateChanneladdEventListenerRequest](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerRequest.schema.json) +- [privateChanneladdEventListenerResponse](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerResponse.schema.json) + +Event messages used to deliver events that have occurred: + +- [privateChannelOnAddContextListenerEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnAddContextListenerEvent.schema.json) +- [privateChannelOnDisconnectEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnDisconnectEvent.schema.json) +- [privateChannelOnUnsubscribeEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnUnsubscribeEvent.schema.json) + +Message exchange for removing the event listener: + +- [privateChannelUnsubscribeEventListenerRequest](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerRequest.schema.json) +- [privateChannelUnsubscribeEventListenerResponse](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerResponse.schema.json) + +#### `disconnect()` + +Request and response used to implement the API call: -### Desktop Agent and Channel API +- [privateChannelDisconnectRequest](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectRequest.schema.json) +- [privateChannelDisconnectResponse](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectResponse.schema.json) -### PrivateChannels +### Checking apps are alive -### User Interfaces +- [heartbeatEvent](https://fdc3.finos.org/schemas/next/api/heartbeatEvent.schema.json) +- [heartbeatAcknowledgment](https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgment.schema.json) +### Controlling injected User Interfaces +- [iFrameMessage](https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json) +- [iFrameHello](https://fdc3.finos.org/schemas/next/api/iFrameHello.schema.json) +- [iFrameHandshake](https://fdc3.finos.org/schemas/next/api/iFrameHandshake.schema.json) +- [iFrameDrag](https://fdc3.finos.org/schemas/next/api/iFrameDrag.schema.json) +- [iFrameRestyle](https://fdc3.finos.org/schemas/next/api/iFrameRestyle.schema.json) +- [iFrameChannels](https://fdc3.finos.org/schemas/next/api/iFrameChannels.schema.json) +- [iFrameChannelSelected](https://fdc3.finos.org/schemas/next/api/iFrameChannelSelected.schema.json) +- [iFrameResolve](https://fdc3.finos.org/schemas/next/api/iFrameResolve.schema.json) +- [iFrameResolveAction](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 041b3d2f6..cbaf5599a 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -37,9 +37,40 @@ Further details for implementing Preload Desktop Agents (which use a Desktop Age ::: -## WCP Message Schemas and Types +## WCP Message Schemas -There are a number of message formats defined as part of the Web Connection Protocol, which will be referenced later in this document, these are: +There are a number of messages defined as part of the Web Connection Protocol. Definitions are provided in [JSON Schema](https://json-schema.org/) in the [FDC3 github repository](https://github.com/finos/FDC3/tree/fdc3-for-web/schemas/api). + +:::tip + +TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): + +```ts +import {BrowserTypes} from '@finos.fdc3'; +``` + +::: + +WCP messages are derived from a base schema, [WCPConnectionStep](https://fdc3.finos.org/schemas/next/api/.schema.json), which defines a common structure for the messages: + +```json +{ + "type": "string", // string identifying the message type + "payload": { + //message payload fields defined for each message type + }, + "meta": { + "connectionAttemptUuid": "79be3ff9-7c05-4371-842a-cf08427c174d", + "timestamp": "2024-09-17T10:15:39+00:00" + } +} +``` + +A value for `meta.connectionAttemptUuid` should be generated as a version 4 UUID according to [IETF RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122) at the start for the connection process and quoted in all subsequent messages, as described later in this document. + +`meta.timestamp` fields are formatted as strings, according to the format defined by [ISO 8601-1:2019](https://www.iso.org/standard/70907.html), which is produced in JavaScript via the `Date` class's `toISOString()` function, e.g. `(new Date()).toISOString()`. + +Messages defined as part of the Web Connection Protocol, which will be referenced later in this document, these are: - [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) - [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index d616052db..6cc8dca20 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -213,7 +213,7 @@ module.exports={ }, "mermaid": { "options": { - "htmlLabels": false, + "htmlLabels": true, "markdownAutoWrap": true, "wrap": true, "wrappingWidth": 50, @@ -227,7 +227,7 @@ module.exports={ "rankSpacing": 50, "diagramPadding": 5, "useMaxWidth": true, - "htmlLabels": false, + "htmlLabels": true, "wrappingWidth": 50 } } From 45a737fed3b446117733f1ba1981e0f226208e2a Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 13 Sep 2024 13:29:28 +0100 Subject: [PATCH 103/152] Complete first draft of DACP protocol docs --- .../api/specs/browserResidentDesktopAgents.md | 2 +- .../desktopAgentCommunicationProtocol.md | 333 +++++++++++------- docs/api/specs/webConnectionProtocol.md | 36 +- schemas/api/iFrameHandshake.schema.json | 2 +- schemas/api/iFrameHello.schema.json | 2 +- src/api/BrowserTypes.ts | 141 ++------ 6 files changed, 266 insertions(+), 250 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index aa6abe3f6..99579885d 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -206,7 +206,7 @@ Checking whether an application has closed may be achieved by a number of approa - If an equivalent `WindowProxy` object (`WindowProxy` objects can be compared with `==` and will be equivalent if they represent the same window) is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. - By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation MUST attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. -- By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. +- By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. Finally, Desktop Agents SHOULD retain instance details for applications that have closed as they may appear to close during navigation events, or may navigate away and then navigate back. By retaining the instance data (`instanceId`, `instanceUuid` and `WindowProxy`) the same instance identity can be maintained or reissued. There is no standard length of time that such details should be retained, hance, Desktop Agents MAY determine for themselves how long to retain instance details for closed instances. diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index a89886343..36ac16e99 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -23,25 +23,25 @@ DACP messages are defined in [JSON Schema](https://json-schema.org/) in the [FDC TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import {BrowserTypes} from '@finos.fdc3'; +import {BrowserTypes} from '@finos/fdc3'; ``` ::: The protocol is composed of several different classes of message, each governed by a message schema: -1. **App Request Messages** ([schema](https://fdc3.finos.org/schemas/next/api/appRequest.schema.json)): +1. **App Request Messages** ([`AppRequest` schema](https://fdc3.finos.org/schemas/next/api/appRequest.schema.json)): - Messages sent by an application representing an API call, such as [`DesktopAgent.broadcast`](../ref/DesktopAgent#broadcast), [`Channel.addContextListener`](../ref/Channel#addcontextlistener), or [`Listener.unsubscribe`](../ref/Types#listener). - Message names all end in 'Request'. - Each instance of a request message sent is uniquely identified by a `meta.requestUuid` field. -2. **Agent Response Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json)): +2. **Agent Response Messages** ([`AgentResponse` schema](https://fdc3.finos.org/schemas/next/api/agentResponse.schema.json)): - Response messages sent from the DA to the application, each relating to a corresponding _App Request Message_. - Message names all end in 'Response'. - Each instance of an Agent Response Message is uniquely identified by a `meta.responseUuid` field. - Each instance of an Agent Response Message quotes the `meta.requestUuid` value of the message it is responding to. -3. **Agent Event Messages** ([schema](https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json)): +3. **Agent Event Messages** ([`AgentEvent` schema](https://fdc3.finos.org/schemas/next/api/agentEvent.schema.json)): - Messages sent from the DA to the application that are due to actions in other applications, such as an inbound context resulting from another app's broadcast. - Message names all end in 'Event'. - Each instance of an Agent Response Message is uniquely identified by a `meta.eventUuid` field. @@ -83,201 +83,202 @@ All messages defined in the DACP follow a common structure: ## Message Definitions Supporting FDC3 API calls -This section provides details of the messages defined in the DACP, grouped according to the FDC3 API functions that they support, and defined by JSON Schema files. Many of these message definitions make use of JSON versions of [metadata](../ref/Metadata) and other [types](../ref/Types) defined by the Desktop Agent API, the JSON versions of which can be found in [api.schema.json](https://fdc3.finos.org/schemas/next/api/api.schema.json), while a number of DACP specific object definitions that are reused through the messages can be found in [common.schema.json](https://fdc3.finos.org/schemas/next/api/common.schema.json) +This section provides details of the messages defined in the DACP, grouped according to the FDC3 API functions that they support, and defined by JSON Schema files. Many of these message definitions make use of JSON versions of [metadata](../ref/Metadata) and other [types](../ref/Types) defined by the Desktop Agent API, the JSON versions of which can be found in [api.schema.json](https://fdc3.finos.org/schemas/next/api/api.schema.json), while a number of DACP specific object definitions that are reused through the messages can be found in [common.schema.json](https://fdc3.finos.org/schemas/next/api/common.schema.json). ### `DesktopAgent` #### `addContextListener()` -Request and response used to implement the API call: -- [addContextListenerRequest](https://fdc3.finos.org/schemas/next/api/addContextListenerRequest.schema.json) -- [addContextListenerResponse](https://fdc3.finos.org/schemas/next/api/addContextListenerResponse.schema.json) +Request and response used to implement the [`DesktopAgent.addContextListener()`](../ref/DesktopAgent#addcontextlistener) and [`Channel.addContextListener()`](../ref/Channel#addcontextlistener) API calls: -Event message used to context objects that have been broadcast: -- [broadcastEvent](https://fdc3.finos.org/schemas/next/api/broadcastEvent.schema.json) +- [`addContextListenerRequest`](https://fdc3.finos.org/schemas/next/api/addContextListenerRequest.schema.json) +- [`addContextListenerResponse`](https://fdc3.finos.org/schemas/next/api/addContextListenerResponse.schema.json) -Message exchange for removing the context listener: -- [contextListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeRequest.schema.json) -- [contextListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeResponse.schema.json) +Event message used to deliver context objects that have been broadcast to listeners: + +- [`broadcastEvent`](https://fdc3.finos.org/schemas/next/api/broadcastEvent.schema.json) + +Request and response for removing the context listener ([`Listener.unsubscribe()`](../ref/Types#listener)): + +- [`contextListenerUnsubscribeRequest`](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeRequest.schema.json) +- [`contextListenerUnsubscribeResponse`](https://fdc3.finos.org/schemas/next/api/contextListenerUnsubscribeResponse.schema.json) #### `addEventListener()` -Request and response used to implement the API call: -- [addEventListenerRequest](https://fdc3.finos.org/schemas/next/api/addEventListenerRequest.schema.json) -- [addEventListenerResponse](https://fdc3.finos.org/schemas/next/api/addEventListenerResponse.schema.json) +Request and response used to implement the [`addEventListener()`](../ref/DesktopAgent#addeventlistener) API call: + +- [`addEventListenerRequest`](https://fdc3.finos.org/schemas/next/api/addEventListenerRequest.schema.json) +- [`addEventListenerResponse`](https://fdc3.finos.org/schemas/next/api/addEventListenerResponse.schema.json) Event messages used to deliver events that have occurred: -- [channelChangedEvent](https://fdc3.finos.org/schemas/next/api/channelChangedEvent.schema.json) +- [`channelChangedEvent`](https://fdc3.finos.org/schemas/next/api/channelChangedEvent.schema.json) -Message exchange for removing the event listener: +Request and response for removing the event listener ([`Listener.unsubscribe()`](../ref/Types#listener)): -- [eventListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeRequest.schema.json) -- [eventListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeResponse.schema.json) +- [`eventListenerUnsubscribeRequest`](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeRequest.schema.json) +- [`eventListenerUnsubscribeResponse`](https://fdc3.finos.org/schemas/next/api/eventListenerUnsubscribeResponse.schema.json) #### `addIntentListener()` -Request and response used to implement the API call: +Request and response used to implement the [`addIntentListener()`](../ref/DesktopAgent#addintentlistener) API call: -- [addIntentListenerRequest](https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json) -- [addIntentListenerResponse](https://fdc3.finos.org/schemas/next/api/addIntentListenerResponse.schema.json) +- [`addIntentListenerRequest`](https://fdc3.finos.org/schemas/next/api/addIntentListenerRequest.schema.json) +- [`addIntentListenerResponse`](https://fdc3.finos.org/schemas/next/api/addIntentListenerResponse.schema.json) Event message used to a raised intent and context object from another app to the listener: -- [intentEvent](https://fdc3.finos.org/schemas/next/api/intentEvent.schema.json) - -An additional request and response used to deliver a result from the intent handler to the Desktop Agent, so that it can convey it back to the raising application: - -- [intentResultRequest](https://fdc3.finos.org/schemas/next/api/intentResultRequest.schema.json) -- [intentResultResponse](https://fdc3.finos.org/schemas/next/api/intentResultResponse.schema.json) +- [`intentEvent`](https://fdc3.finos.org/schemas/next/api/intentEvent.schema.json) -Message exchange for removing the intent listener: +An additional request and response used to deliver an [`IntentResult`](../ref/Types#intentresult) from the intent handler to the Desktop Agent, so that it can convey it back to the raising application: -- [intentListenerUnsubscribeRequest](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeRequest.schema.json) -- [intentListenerUnsubscribeResponse](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json) +- [`intentResultRequest`](https://fdc3.finos.org/schemas/next/api/intentResultRequest.schema.json) +- [`intentResultResponse`](https://fdc3.finos.org/schemas/next/api/intentResultResponse.schema.json) +Please note this exchange (and the `IntentResolution.getResult()` API call) support `void` results from a raised intent and hence this message exchange should occur for all raised intents, including those that do not return a result. In such cases, the void intent result allows resolution of the `IntentResolution.getResult()` API call and indicates that the intent handler has finished running. +Request and response for removing the intent listener ([`Listener.unsubscribe()`](../ref/Types#listener)):: +- [`intentListenerUnsubscribeRequest`](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeRequest.schema.json) +- [`intentListenerUnsubscribeResponse`](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json) -```mermaid -sequenceDiagram - AppA ->> DesktopAgent: raiseIntentRequest - DesktopAgent ->> AppB: intentEvent - DesktopAgent ->> AppA: raiseIntentResponse -``` +A typical exchange of messages between an app raising an intent, a Desktop agent and an app resolving an intent is: ```mermaid sequenceDiagram AppA ->> DesktopAgent: raiseIntentRequest DesktopAgent ->> AppB: intentEvent DesktopAgent ->> AppA: raiseIntentResponse - break when AppIntent return with multiple options - DesktopAgent-->AppA: getAgent displays IntentResolver - AppA-->DesktopAgent: User picks an option - end - AppA ->> DesktopAgent: raiseIntentRequest - Note left of AppA: New request includes specific `app` target
/and new requestUuid - DesktopAgent ->> AppB: intentEvent - DesktopAgent ->> AppA: raiseIntentResponse AppB ->> DesktopAgent: intentResultRequest DesktopAgent ->> AppB: intentResultResponse DesktopAgent ->> AppA: raiseIntentResultResponse ``` +The above flow assumes that AppB has already been launched and added an intent listener. As apps can be launched to resolve an intent a typical message exchange (that includes registration of the intent listener) is: + ```mermaid sequenceDiagram AppA ->> DesktopAgent: raiseIntentRequest - DesktopAgent ->> AppB: intentEvent - break DA determines there are multiple options - DesktopAgent-->AppA: Desktop Agent displays IntentResolver - AppA-->DesktopAgent: User picks an option + break intent resolution determines a new instance of AppB should be launched + DesktopAgent -->> AppB: Launch + AppB -->> DesktopAgent: Connect via WCP end + AppB ->> DesktopAgent: addIntentListenerRequest + DesktopAgent ->> AppB: addIntentListenerResponse + DesktopAgent ->> AppB: intentEvent DesktopAgent ->> AppA: raiseIntentResponse AppB ->> DesktopAgent: intentResultRequest DesktopAgent ->> AppB: intentResultResponse DesktopAgent ->> AppA: raiseIntentResultResponse ``` +:::tip + +See [`raiseIntent`](#raiseintent) below for further examples of message exchanges involved in raising intents and intent resolution. + +::: + #### `broadcast()` -Request and response used to implement the API call: +Request and response used to implement the [`DesktopAgent.broadcast()`](../ref/DesktopAgent#broadcast) and [`Channel.broadcast()`](../ref/Channel#broadcast) API calls: -- [broadcastRequest](https://fdc3.finos.org/schemas/next/api/broadcastRequest.schema.json) -- [broadcastResponse](https://fdc3.finos.org/schemas/next/api/broadcastResponse.schema.json) +- [`broadcastRequest`](https://fdc3.finos.org/schemas/next/api/broadcastRequest.schema.json) +- [`broadcastResponse`](https://fdc3.finos.org/schemas/next/api/broadcastResponse.schema.json) See [`addContextListener()`](#addcontextlistener) above for the `broadcastEvent` used to deliver the broadcast to other apps. #### `createPrivateChannel()` -Request and response used to implement the API call: +Request and response used to implement the [`createPrivateChannel()`](../ref/DesktopAgent#createprivatechannel) API call: -- [createPrivateChannelRequest](https://fdc3.finos.org/schemas/next/api/createPrivateChannelRequest.schema.json) -- [createPrivateChannelResponse](https://fdc3.finos.org/schemas/next/api/createPrivateChannelResponse.schema.json) +- [`createPrivateChannelRequest`](https://fdc3.finos.org/schemas/next/api/createPrivateChannelRequest.schema.json) +- [`createPrivateChannelResponse`](https://fdc3.finos.org/schemas/next/api/createPrivateChannelResponse.schema.json) #### `findInstances()` -Request and response used to implement the API call: +Request and response used to implement the [`findInstances()`](../ref/DesktopAgent#findinstances) API call: -- [findInstancesRequest](https://fdc3.finos.org/schemas/next/api/findInstancesRequest.schema.json) -- [findInstancesResponse](https://fdc3.finos.org/schemas/next/api/findInstancesResponse.schema.json) +- [`findInstancesRequest`](https://fdc3.finos.org/schemas/next/api/findInstancesRequest.schema.json) +- [`findInstancesResponse`](https://fdc3.finos.org/schemas/next/api/findInstancesResponse.schema.json) #### `findIntent()` -Request and response used to implement the API call: +Request and response used to implement the [`findIntent()`](../ref/DesktopAgent#findintent) API call: -- [findIntentRequest](https://fdc3.finos.org/schemas/next/api/findIntentRequest.schema.json) -- [findIntentResponse](https://fdc3.finos.org/schemas/next/api/findIntentResponse.schema.json) +- [`findIntentRequest`](https://fdc3.finos.org/schemas/next/api/findIntentRequest.schema.json) +- [`findIntentResponse`](https://fdc3.finos.org/schemas/next/api/findIntentResponse.schema.json) #### `findIntentsByContext()` -Request and response used to implement the API call: +Request and response used to implement the [`findIntentsByContext()`](../ref/DesktopAgent#findintentsbycontext) API call: -- [findIntentsByContextRequest](https://fdc3.finos.org/schemas/next/api/findIntentsByContextRequest.schema.json) -- [findIntentsByContextResponse](https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json) +- [`findIntentsByContextRequest`](https://fdc3.finos.org/schemas/next/api/findIntentsByContextRequest.schema.json) +- [`findIntentsByContextResponse`](https://fdc3.finos.org/schemas/next/api/findIntentsByContextResponse.schema.json) #### `getAppMetadata()` -Request and response used to implement the API call: +Request and response used to implement the [`getAppMetadata()`](../ref/DesktopAgent#getappmetadata) API call: -- [getAppMetadataRequest](https://fdc3.finos.org/schemas/next/api/getAppMetadataRequest.schema.json) -- [getAppMetadataResponse](https://fdc3.finos.org/schemas/next/api/getAppMetadataResponse.schema.json) +- [`getAppMetadataRequest`](https://fdc3.finos.org/schemas/next/api/getAppMetadataRequest.schema.json) +- [`getAppMetadataResponse`](https://fdc3.finos.org/schemas/next/api/getAppMetadataResponse.schema.json) #### `getCurrentChannel()` -Request and response used to implement the API call: +Request and response used to implement the [`getCurrentChannel()`](../ref/DesktopAgent#getcurrentchannel) API call: -- [getCurrentChannelRequest](https://fdc3.finos.org/schemas/next/api/getCurrentChannelRequest.schema.json) -- [getCurrentChannelResponse](https://fdc3.finos.org/schemas/next/api/getCurrentChannelResponse.schema.json) +- [`getCurrentChannelRequest`](https://fdc3.finos.org/schemas/next/api/getCurrentChannelRequest.schema.json) +- [`getCurrentChannelResponse`](https://fdc3.finos.org/schemas/next/api/getCurrentChannelResponse.schema.json) #### `getInfo()` -Request and response used to implement the API call: +Request and response used to implement the [`getInfo()`](../ref/DesktopAgent#getinfo) API call: -- [getInfoRequest](https://fdc3.finos.org/schemas/next/api/getInfoRequest.schema.json) -- [getInfoResponse](https://fdc3.finos.org/schemas/next/api/getInfoResponse.schema.json) +- [`getInfoRequest`](https://fdc3.finos.org/schemas/next/api/getInfoRequest.schema.json) +- [`getInfoResponse`](https://fdc3.finos.org/schemas/next/api/getInfoResponse.schema.json) #### `getOrCreateChannel()` -Request and response used to implement the API call: +Request and response used to implement the [`getOrCreateChannel()`](../ref/DesktopAgent#getorcreatechannel] API call: -- [getOrCreateChannelRequest](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelRequest.schema.json) -- [getOrCreateChannelResponse](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelResponse.schema.json) +- [`getOrCreateChannelRequest`](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelRequest.schema.json) +- [`getOrCreateChannelResponse`](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelResponse.schema.json) #### `getUserChannels()` -Request and response used to implement the API call: +Request and response used to implement the [`getUserChannels()`](../ref/DesktopAgent#getuserchannels) API call: -- [getUserChannelsRequest](https://fdc3.finos.org/schemas/next/api/getUserChannelsRequest.schema.json) -- [getUserChannelsResponse](https://fdc3.finos.org/schemas/next/api/getUserChannelsResponse.schema.json) +- [`getUserChannelsRequest`](https://fdc3.finos.org/schemas/next/api/getUserChannelsRequest.schema.json) +- [`getUserChannelsResponse`](https://fdc3.finos.org/schemas/next/api/getUserChannelsResponse.schema.json) #### `joinUserChannel()` -Request and response used to implement the API call: +Request and response used to implement the [`joinUserChannel()`](../ref/DesktopAgent#joinchannel) API call: -- [joinUserChannelRequest](https://fdc3.finos.org/schemas/next/api/joinUserChannelRequest.schema.json) -- [joinUserChannelResponse](https://fdc3.finos.org/schemas/next/api/joinUserChannelResponse.schema.json) +- [`joinUserChannelRequest`](https://fdc3.finos.org/schemas/next/api/joinUserChannelRequest.schema.json) +- [`joinUserChannelResponse`](https://fdc3.finos.org/schemas/next/api/joinUserChannelResponse.schema.json) #### `leaveCurrentChannel()` -Request and response used to implement the API call: +Request and response used to implement the [`leaveCurrentChannel()`](../ref/DesktopAgent#leavecurrentchannel) API call: -- [leaveCurrentChannelRequest](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelRequest.schema.json) -- [leaveCurrentChannelResponse](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelResponse.schema.json) +- [`leaveCurrentChannelRequest`](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelRequest.schema.json) +- [`leaveCurrentChannelResponse`](https://fdc3.finos.org/schemas/next/api/leaveCurrentChannelResponse.schema.json) #### `open()` -Request and response used to implement the API call: +Request and response used to implement the [`open()`](../ref/DesktopAgent#open) API call: -- [openRequest](https://fdc3.finos.org/schemas/next/api/openRequest.schema.json) -- [openResponse](https://fdc3.finos.org/schemas/next/api/openResponse.schema.json) +- [`openRequest`](https://fdc3.finos.org/schemas/next/api/openRequest.schema.json) +- [`openResponse`](https://fdc3.finos.org/schemas/next/api/openResponse.schema.json) Where a context object is passed (e.g. `fdc3.open(app, context)`) the `broadcastEvent` message described above in [`addContextListener`](#addcontextlistener) should be used to deliver it after the context listener has been added: ```mermaid sequenceDiagram AppA ->> DesktopAgent: openRequest
(with context) - break Desktop Agent spawns AppB - AppB-->DesktopAgent: AppB connects to DesktopAgent + break Desktop Agent launches AppB + DesktopAgent -->> AppB: Launch + AppB -->> DesktopAgent: Connect via WCP end AppB ->> DesktopAgent: addContextListenerRequest DesktopAgent ->> AppB: addContextListenerResponse @@ -286,22 +287,79 @@ sequenceDiagram #### `raiseIntent()` -Request and response used to implement the API call: +Request and response used to implement the [`raiseIntent()`](../ref/DesktopAgent#raiseintent) API call: + +- [`raiseIntentRequest`](https://fdc3.finos.org/schemas/next/api/raiseIntentRequest.schema.json) +- [`raiseIntentResponse`](https://fdc3.finos.org/schemas/next/api/raiseIntentResponse.schema.json) + +An additional response message is provided for the delivery of an `IntentResult` from the resolving application to the raising application (which is collected via the [`IntentResolution.getResult()`](../ref/Metadata#intentresolution) API call), which should quote the `requestUuid` from the original `raiseIntentRequest`: + +- [`raiseIntentResultResponse`](https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json) + +:::tip + +See [`addIntentListener`](#addintentlistener) above for details of the messages used for the resolving app to deliver the result to the Desktop Agent. + +::: + +Where there are multiple options for resolving a raised intents, there are two possible versions of the resulting message exchanges. Which to use depends on whether the Desktop Agent uses an intent resolver user interface (or other suitable mechanism) that it controls, or one injected into the application (for example an iframe injected by a `getAgent()` implementation into an application window) to perform resolution. -- [raiseIntentRequest](https://fdc3.finos.org/schemas/next/api/raiseIntentRequest.schema.json) -- [raiseIntentResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentResponse.schema.json) -- [raiseIntentResultResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json) +When working with an injected interface, the Desktop Agent should respond with a `raiseIntentResponse` containing a `RaiseIntentNeedsResolutionResponsePayload`: + +```mermaid +--- +title: Intent resolution with injected Intent Resolver iframe +--- +sequenceDiagram + AppA ->> DesktopAgent: raiseIntentRequest + DesktopAgent ->> AppB: intentEvent + DesktopAgent ->> AppA: raiseIntentResponse + Note left of DesktopAgent: raiseIntentResponse includes a
RaiseIntentNeedsResolutionResponsePayload
containing an AppIntent + break when AppIntent return with multiple options + DesktopAgent --> AppA: getAgent displays IntentResolver + AppA --> DesktopAgent: User picks an option + end + AppA ->> DesktopAgent: raiseIntentRequest + Note left of DesktopAgent: New request includes a
specific 'app' target
and new requestUuid + DesktopAgent ->> AppB: intentEvent + DesktopAgent ->> AppA: raiseIntentResponse + AppB ->> DesktopAgent: intentResultRequest + DesktopAgent ->> AppB: intentResultResponse + DesktopAgent ->> AppA: raiseIntentResultResponse +``` + +Alternatively, if the Desktop Agent is able to provide its own user interface or another suitable means of resolving the intent, then it may do so and respond with a `raiseIntentResponse` containing a `RaiseIntentSuccessResponsePayload`: + +```mermaid +--- +title: Intent resolution with Desktop Agent provided Intent Resolver +--- +sequenceDiagram + AppA ->> DesktopAgent: raiseIntentRequest + DesktopAgent ->> AppB: intentEvent + break DA determines there are multiple options + DesktopAgent-->AppA: Desktop Agent displays an
IntentResolver UI + AppA-->DesktopAgent: User picks an option + end + DesktopAgent ->> AppA: raiseIntentResponse + Note left of DesktopAgent: DesktopAgent responds
to the original
raiseIntentRequest message with
a RaiseIntentSuccessResponsePayload + AppB ->> DesktopAgent: intentResultRequest + DesktopAgent ->> AppB: intentResultResponse + DesktopAgent ->> AppA: raiseIntentResultResponse +``` #### `raiseIntentForContext()` -Request and response used to implement the API call: +Request and response used to implement the [`raiseIntentForContext()`](../ref/DesktopAgent#raiseintentforcontext) API call: + +- [`raiseIntentForContextRequest`](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextRequest.schema.json) +- [`raiseIntentForContextResponse`](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextResponse.schema.json) -- [raiseIntentForContextRequest](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextRequest.schema.json) -- [raiseIntentForContextResponse](https://fdc3.finos.org/schemas/next/api/raiseIntentForContextResponse.schema.json) +Message exchanges for handling `raiseIntentForContext()` are the same as for `raiseIntent`, except for the substitution of `raiseIntentForContextRequest` for `raiseIntentRequest` and `raiseIntentForContextResponse` for `raiseIntentResponse`. Hence, please see [`raiseIntent`](#raiseintent) and [`addIntentListener`](#addintentlistener) for further details. ### `Channel` -Owing to the significant overlap between the FDC3 `DesktopAgent` and `Channel` API, which includes the ability to retrieve and work with User channels as App Channels, most of the messaging for the `Channel` API is shared with `DesktopAgent`. Specifically, all messages defined in the the [`broadcast`](#broadcast) and [`addContextListener`](#addcontextlistener) sections above are reused, with a few minor differences to note: +Owing to the significant overlap between the FDC3 [`DesktopAgent`](../ref/DesktopAgent) and [`Channel`](../ref/Channel) interfaces, which includes the ability to retrieve and work with User channels as App Channels, most of the messaging for the `Channel` API is shared with `DesktopAgent`. Specifically, all messages defined in the the [`broadcast`](#broadcast) and [`addContextListener`](#addcontextlistener) sections above are reused, with a few minor differences to note: - When working with a specific channel, the `channelId` property in `addContextListenerRequest` should be set to the ID of the channel, where it is set to `null` to work with the current user channel. - When receiving a `broadcastEvent` a `channelId` that is `null` indicates that the context was sent via a call to `fdc3.open` and does not relate to a channel. @@ -310,55 +368,78 @@ The following additional function is unique to the `Channel` interface: #### `getCurrentContext()` -Request and response used to implement the API call: +Request and response used to implement the [`Channel.getCurrentContext()`](../ref/Channel#getcurrentcontext) API call: -- [getCurrentContextRequest](https://fdc3.finos.org/schemas/next/api/getCurrentContextRequest.schema.json) -- [getCurrentContextResponse](https://fdc3.finos.org/schemas/next/api/getCurrentContextResponse.schema.json) +- [`getCurrentContextRequest`](https://fdc3.finos.org/schemas/next/api/getCurrentContextRequest.schema.json) +- [`getCurrentContextResponse`](https://fdc3.finos.org/schemas/next/api/getCurrentContextResponse.schema.json) ### `PrivateChannel` -The `PrivateChannel` interface extends `Channel` with a number of additional functions that are supported by the following messages: +The [`PrivateChannel`](../ref/PrivateChannel) interface extends [`Channel`](../ref/Channel) with a number of additional functions that are supported by the following messages: #### `addEventListener()` -Request and response used to implement the API call: +Request and response used to implement the [`PrivateChannel.addEventListener`](../ref/PrivateChannel#addeventlistener) API call: -- [privateChanneladdEventListenerRequest](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerRequest.schema.json) -- [privateChanneladdEventListenerResponse](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerResponse.schema.json) +- [`privateChanneladdEventListenerRequest`](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerRequest.schema.json) +- [`privateChanneladdEventListenerResponse`](https://fdc3.finos.org/schemas/next/api/privateChanneladdEventListenerResponse.schema.json) Event messages used to deliver events that have occurred: -- [privateChannelOnAddContextListenerEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnAddContextListenerEvent.schema.json) -- [privateChannelOnDisconnectEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnDisconnectEvent.schema.json) -- [privateChannelOnUnsubscribeEvent](https://fdc3.finos.org/schemas/next/api/privateChannelOnUnsubscribeEvent.schema.json) +- [`privateChannelOnAddContextListenerEvent`](https://fdc3.finos.org/schemas/next/api/privateChannelOnAddContextListenerEvent.schema.json) +- [`privateChannelOnDisconnectEvent`](https://fdc3.finos.org/schemas/next/api/privateChannelOnDisconnectEvent.schema.json) +- [`privateChannelOnUnsubscribeEvent`](https://fdc3.finos.org/schemas/next/api/privateChannelOnUnsubscribeEvent.schema.json) -Message exchange for removing the event listener: +:::tip -- [privateChannelUnsubscribeEventListenerRequest](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerRequest.schema.json) -- [privateChannelUnsubscribeEventListenerResponse](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerResponse.schema.json) +The above messages may also be used to implement the deprecated [`onAddContextListener()`](../ref/PrivateChannel#onaddcontextlistener), [`onUnsubscribe`](../ref/PrivateChannel#onunsubscribe) and [`onDisconnect`](../ref/PrivateChannel#ondisconnect) functions of the `PrivateChannel` interface. + +::: + +Message exchange for removing the event listener [`Listener.unsubscribe`](../ref/Types#listener): + +- [`privateChannelUnsubscribeEventListenerRequest`](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerRequest.schema.json) +- [`privateChannelUnsubscribeEventListenerResponse`](https://fdc3.finos.org/schemas/next/api/privateChannelUnsubscribeEventListenerResponse.schema.json) #### `disconnect()` -Request and response used to implement the API call: +Request and response used to implement the [`PrivateChannel.disconnect()`](../ref/PrivateChannel#disconnect) API call: -- [privateChannelDisconnectRequest](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectRequest.schema.json) -- [privateChannelDisconnectResponse](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectResponse.schema.json) +- [`privateChannelDisconnectRequest`](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectRequest.schema.json) +- [`privateChannelDisconnectResponse`](https://fdc3.finos.org/schemas/next/api/privateChannelDisconnectResponse.schema.json) ### Checking apps are alive -- [heartbeatEvent](https://fdc3.finos.org/schemas/next/api/heartbeatEvent.schema.json) -- [heartbeatAcknowledgment](https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgment.schema.json) +Depending on the connection over which the Desktop Agent and app are connected, it may be necessary for the Desktop Agent to check whether the application is still alive. This can be done, either periodically or on demand (for example to validate options that will be provided in an [`AppIntent`](../ref/Metadata#appintent) as part of a `findIntentResponse` or `raiseIntentResponse` and displayed in an intent resolver interface), using the following message exchange: + +- [`heartbeatEvent`](https://fdc3.finos.org/schemas/next/api/heartbeatEvent.schema.json) +- [`heartbeatAcknowledgment`](https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgment.schema.json) + +As a Desktop Agent initiated exchange, it is initiated with an `AgentEvent` message and completed via an `AppRequest` message as an acknowledgement. + +:::tip + +Additional procedures are defined in the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects) and [Web Connection Protocol](./webConnectionProtocol#step-5-disconnection) for the detection of app disconnection or closure. Implementations will often need to make use of multiple procedures to catch all forms of disconnection in a web browser. + +::: ### Controlling injected User Interfaces -- [iFrameMessage](https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json) -- [iFrameHello](https://fdc3.finos.org/schemas/next/api/iFrameHello.schema.json) -- [iFrameHandshake](https://fdc3.finos.org/schemas/next/api/iFrameHandshake.schema.json) -- [iFrameDrag](https://fdc3.finos.org/schemas/next/api/iFrameDrag.schema.json) -- [iFrameRestyle](https://fdc3.finos.org/schemas/next/api/iFrameRestyle.schema.json) +Desktop Agent implementations, such as those based on the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents) and [Web Connection Protocol](./webConnectionProtocol), may either provide their own user interfaces (or other appropriate mechanisms) for the selection of User Channels or Intent Resolution, or they may work with implementations injected into the application (for example, as described in the [Web Connection Protocol](./webConnectionProtocol#providing-channel-selector-and-intent-resolver-uis) and implemented in [`getAgent()`](../ref/GetAgent)). + +Where injected user interfaces are used, standardized messaging is needed to communicate with those interfaces. This is provided in the DACP via the following 'iframe' messages, which are governed by the [`iFrameMessage`](https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json) schema. The following messages are provided: + +- [`iFrameHello`](https://fdc3.finos.org/schemas/next/api/iFrameHello.schema.json): Sent by the iframe to its `window.parent` frame to initiate communication and to provide initial CSS to apply to the frame. This message should have a `MessagePort` appended over which further communication will be conducted. +- [`iFrameHandshake`](https://fdc3.finos.org/schemas/next/api/iFrameHandshake.schema.json): Response to the `iFrameHello` message sent by the application frame, which should be sent over the `MessagePort`. Includes details of the FDC3 version that the application is using. +- [`iFrameDrag`](https://fdc3.finos.org/schemas/next/api/iFrameDrag.schema.json): Message sent by the iframe to indicate that it is being dragged to a new position and including offsets to indicate direction and distance. +- [`iFrameRestyle`](https://fdc3.finos.org/schemas/next/api/iFrameRestyle.schema.json): Message sent by the iframe to indicate that its frame should have updated CSS applied to it, for example to support a channel selector interface that can be 'popped open' or an intent resolver that wishes to resize itself to show additional content. + +Messages are also provided that are specific to each interface type provided by a Desktop Agent. The following messages are specific to Channel Selector user interfaces: + +- [`iFrameChannels`](https://fdc3.finos.org/schemas/next/api/iFrameChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `iFrameHandshake` and before making the injected iframe visible. +- [`iFrameChannelSelected`](https://fdc3.finos.org/schemas/next/api/iFrameChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. -- [iFrameChannels](https://fdc3.finos.org/schemas/next/api/iFrameChannels.schema.json) -- [iFrameChannelSelected](https://fdc3.finos.org/schemas/next/api/iFrameChannelSelected.schema.json) +Messages specific to Intent Resolver user interfaces: -- [iFrameResolve](https://fdc3.finos.org/schemas/next/api/iFrameResolve.schema.json) -- [iFrameResolveAction](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json) +- [`iFrameResolve`](https://fdc3.finos.org/schemas/next/api/iFrameResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. +- [`iFrameResolveAction`](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementaiton after a resolution option is selected. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index cbaf5599a..ab1eef269 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -194,7 +194,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. If no response is received from any candidate, the `getAgent()` implementation MAY retry sending the `WCP1Hello` message periodically until the timeout is reached. 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: - * Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: + - Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: ```ts const loadIframe = (url, loadedHandler): WindowProxy => { const ifrm = document.createElement("iframe"); @@ -208,10 +208,14 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp return ifrm.contentWindow; } ``` - * Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. + - Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. + :::tip + To ensure that the iframe is ready to receive the `WCP1Hello` message when the `load` event fires, implementations should call `window.addEventListener` for the `message` event synchronously and as early as possible. + ::: + 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have be received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. Add a listener (`port.addEventListener("message", (event) => {})`) to receive messages from the selected `candidate`, before moving on to the next stage. @@ -222,9 +226,12 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp If the failover function resolves to a `DesktopAgent` implementation it should be immediately returned in the same way as a `window.fdc3` reference was and handled as if it represents a Desktop Agent Preload interface. If the failover function resolves to a `WindowProxy` object, repeat steps 1-3 & 5 above substituting the `WindowProxy` for the candidate window objects before proceeding to the next step. - :::tip - Where possible, iframe failover functions should wait for the iframe or window represented by a `WindowProxy` object to be ready to receive messages before resolving. For an iframe this is a case of waiting for the `load` event to fire. - ::: + + :::tip + + Where possible, iframe failover functions should wait for the iframe or window represented by a `WindowProxy` object to be ready to receive messages before resolving. For an iframe this is a case of waiting for the `load` event to fire. + + ::: ### Step 2: Validate app & instance identity @@ -285,7 +292,7 @@ Desktop Agent Preload interfaces, as used in container-based Desktop Agent imple However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. -As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection must also be used, these are described in the [Disconnects section of the Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects). +As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection may also be used, these are described in the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects) and [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). ### `getAgent()` Workflow Diagram @@ -348,16 +355,6 @@ Users of FDC3 Desktop Agents often need access to UI controls that allow them to However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. -The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via: - -- **[WCP1Hello](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json)**: Sent by an application and incorporating boolean `payload.intentResolver` and `payload.channelSelector` fields, which are set to false if either UI is not needed (defaults to true). -- **[WCP3Handshake](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json)**: Response sent by the Desktop Agent and incorporating `payload.intentResolverUrl` and `payload.channelSelectorUrl` fields, which should be set to the URL for each UI implementation, which should be loaded into an iframe to provide the UI (defaults to URLs for reference UI implementations provided by the FDC3 project). - -When UI iframes are created, the user interfaces may use messages incorporated into the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with the `getAgent()` implementation and through it the Desktop Agent. - - -//TODO complete description once finalized. - ```mermaid flowchart LR subgraph DA [Desktop Agent Window] @@ -376,3 +373,10 @@ flowchart LR B-->cs B-->ir ``` + +The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via to following messages: + +- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json): Sent by an application and incorporating boolean `payload.intentResolver` and `payload.channelSelector` fields, which are set to `false` if either UI is not needed (defaults to `true`). +- [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json): Response sent by the Desktop Agent and incorporating `payload.intentResolverUrl` and `payload.channelSelectorUrl` fields, which should be set to the URL for each UI implementation that should be loaded into an iframe to provide the UI (defaults to URLs for reference UI implementations provided by the FDC3 project), or set to `false` to indicate that the respective UI is not needed. Setting these fields to `true` will cause the `getAgent()` implementation to use its default URLs representing a reference implementation of each UI. + +When UI iframes are created, the user interfaces may use the 'iframe' messages incorporated into the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol#controlling-injected-user-interfaces) to communicate with the `getAgent()` implementation and through it the Desktop Agent. diff --git a/schemas/api/iFrameHandshake.schema.json b/schemas/api/iFrameHandshake.schema.json index 4e0b534a3..7d96825f3 100644 --- a/schemas/api/iFrameHandshake.schema.json +++ b/schemas/api/iFrameHandshake.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", "title": "iframe Handshake", - "description": "Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) with a MessagePort appended over further communication is conducted.", + "description": "Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) over the `MessagePort` provide in the preceding iFrameHello message, confirming that it is listening to the `MessagePort` for further communication.", "type": "object", "allOf": [ { diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index d29c86418..78f736492 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeHello.schema.json", "title": "iframe Hello", - "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate and containing initial CSS to set on the iframe.", + "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate, containing initial CSS to set on the iframe and including an appended `MessagePort` to be used for further communication.", "type": "object", "allOf": [ { diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 1a9819296..e268fdaf1 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,10 +1,9 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerEvent, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatAcknowledgementRequest, HeartbeatEvent, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatAcknowledgementRequest, HeartbeatEvent, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); -// const addEventListenerEvent = Convert.toAddEventListenerEvent(json); // const addEventListenerRequest = Convert.toAddEventListenerRequest(json); // const addEventListenerResponse = Convert.toAddEventListenerResponse(json); // const addIntentListenerRequest = Convert.toAddIntentListenerRequest(json); @@ -257,66 +256,6 @@ export interface AddContextListenerResponsePayload { */ export type PurpleError = "AccessDenied" | "CreationFailed" | "MalformedContext" | "NoChannelFound"; -/** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ - -/** - * An event message from the Desktop Agent to an app for a specified event type. - * - * A message from a Desktop Agent to an FDC3-enabled app representing an event. - */ -export interface AddEventListenerEvent { - /** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ - meta: AddEventListenerEventMeta; - /** - * The message payload contains details of the event that the app is being notified about. - */ - payload: AddEventListenerEventPayload; - /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ - type: "addEventListenerEvent"; -} - -/** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. - */ -export interface AddEventListenerEventMeta { - eventUuid: string; - timestamp: Date; -} - -/** - * The message payload contains details of the event that the app is being notified about. - */ -export interface AddEventListenerEventPayload { - event: FDC3Event; -} - -/** - * An event object received via the FDC3 API's addEventListener function. Will always - * include both type and details, which describe type of the event and any additional - * details respectively. - */ -export interface FDC3Event { - /** - * Additional details of the event, such as the `currentChannelId` for a CHANNEL_CHANGED - * event - */ - details: { [key: string]: any }; - type: "USER_CHANNEL_CHANGED"; -} - -/** - * The type of a (non-context and non-intent) event that may be received via the FDC3 API's - * addEventListener function. - */ - /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. @@ -353,6 +292,11 @@ export interface AddEventListenerRequestPayload { type: "USER_CHANNEL_CHANGED" | null; } +/** + * The type of a (non-context and non-intent) event that may be received via the FDC3 API's + * addEventListener function. + */ + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. @@ -629,7 +573,7 @@ export interface BroadcastEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -641,6 +585,14 @@ export interface BroadcastEvent { type: "broadcastEvent"; } +/** + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + */ +export interface BroadcastEventMeta { + eventUuid: string; + timestamp: Date; +} + /** * The message payload contains details of the event that the app is being notified about. */ @@ -818,7 +770,7 @@ export interface ChannelChangedEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2066,7 +2018,7 @@ export interface HeartbeatEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -2210,7 +2162,8 @@ export interface MouseOffsets { /** * Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) - * with a MessagePort appended over further communication is conducted. + * over the `MessagePort` provide in the preceding iFrameHello message, confirming that it + * is listening to the `MessagePort` for further communication. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in @@ -2243,7 +2196,8 @@ export interface IframeHandshakePayload { /** * Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to - * indicate it is ready to communicate and containing initial CSS to set on the iframe. + * indicate it is ready to communicate, containing initial CSS to set on the iframe and + * including an appended `MessagePort` to be used for further communication. * * A message used to communicate with iframes injected by `getAgent()` for displaying UI * elements such as the intent resolver or channel selector. Used for messages sent in @@ -2516,7 +2470,7 @@ export interface IntentEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -3103,7 +3057,7 @@ export interface PrivateChannelOnAddContextListenerEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -3145,7 +3099,7 @@ export interface PrivateChannelOnDisconnectEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -3182,7 +3136,7 @@ export interface PrivateChannelOnUnsubscribeEvent { /** * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - meta: AddEventListenerEventMeta; + meta: BroadcastEventMeta; /** * The message payload contains details of the event that the app is being notified about. */ @@ -3885,14 +3839,6 @@ export class Convert { return JSON.stringify(uncast(value, r("AddContextListenerResponse")), null, 2); } - public static toAddEventListenerEvent(json: string): AddEventListenerEvent { - return cast(JSON.parse(json), r("AddEventListenerEvent")); - } - - public static addEventListenerEventToJson(value: AddEventListenerEvent): string { - return JSON.stringify(uncast(value, r("AddEventListenerEvent")), null, 2); - } - public static toAddEventListenerRequest(json: string): AddEventListenerRequest { return cast(JSON.parse(json), r("AddEventListenerRequest")); } @@ -4713,22 +4659,6 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, { json: "listenerUUID", js: "listenerUUID", typ: u(undefined, "") }, ], false), - "AddEventListenerEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, - { json: "payload", js: "payload", typ: r("AddEventListenerEventPayload") }, - { json: "type", js: "type", typ: r("AddEventListenerEventType") }, - ], false), - "AddEventListenerEventMeta": o([ - { json: "eventUuid", js: "eventUuid", typ: "" }, - { json: "timestamp", js: "timestamp", typ: Date }, - ], false), - "AddEventListenerEventPayload": o([ - { json: "event", js: "event", typ: r("FDC3Event") }, - ], false), - "FDC3Event": o([ - { json: "details", js: "details", typ: m("any") }, - { json: "type", js: "type", typ: r("FDC3EventType") }, - ], false), "AddEventListenerRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("AddEventListenerRequestPayload") }, @@ -4797,10 +4727,14 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "BroadcastEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("BroadcastEventPayload") }, { json: "type", js: "type", typ: r("BroadcastEventType") }, ], false), + "BroadcastEventMeta": o([ + { json: "eventUuid", js: "eventUuid", typ: "" }, + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), "BroadcastEventPayload": o([ { json: "channelId", js: "channelId", typ: u(null, "") }, { json: "context", js: "context", typ: r("Context") }, @@ -4829,7 +4763,7 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("ResponsePayloadError")) }, ], "any"), "ChannelChangedEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("ChannelChangedEventPayload") }, { json: "type", js: "type", typ: r("ChannelChangedEventType") }, ], false), @@ -5096,7 +5030,7 @@ const typeMap: any = { { json: "timestamp", js: "timestamp", typ: Date }, ], false), "HeartbeatEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("HeartbeatEventPayload") }, { json: "type", js: "type", typ: r("HeartbeatEventType") }, ], false), @@ -5197,7 +5131,7 @@ const typeMap: any = { { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, ], "any"), "IntentEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("IntentEventPayload") }, { json: "type", js: "type", typ: r("IntentEventType") }, ], false), @@ -5323,7 +5257,7 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("PurpleError")) }, ], false), "PrivateChannelOnAddContextListenerEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnAddContextListenerEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnAddContextListenerEventType") }, ], false), @@ -5332,7 +5266,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnDisconnectEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnDisconnectEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnDisconnectEventType") }, ], false), @@ -5340,7 +5274,7 @@ const typeMap: any = { { json: "privateChannelId", js: "privateChannelId", typ: "" }, ], false), "PrivateChannelOnUnsubscribeEvent": o([ - { json: "meta", js: "meta", typ: r("AddEventListenerEventMeta") }, + { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("PrivateChannelOnUnsubscribeEventPayload") }, { json: "type", js: "type", typ: r("PrivateChannelOnUnsubscribeEventType") }, ], false), @@ -5502,9 +5436,6 @@ const typeMap: any = { "FDC3EventType": [ "USER_CHANNEL_CHANGED", ], - "AddEventListenerEventType": [ - "addEventListenerEvent", - ], "AddEventListenerRequestType": [ "addEventListenerRequest", ], From c2609fac536a5d744b6b90aadd70f1a2225ffe38 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 13 Sep 2024 13:50:10 +0100 Subject: [PATCH 104/152] Adjust name of Browser-resident DA section --- docs/api/specs/browserResidentDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 99579885d..7955b560d 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -1,7 +1,7 @@ --- id: browserDesktopAgents sidebar_label: Browser Desktop Agents -title: Browser Desktop Agents (next) +title: Browser-Resident Desktop Agents (next) --- :::info _[@experimental](../fdc3-compliance#experimental-features)_ From 809647324ec2f51df4a0a6fdbc2132ce5f97c7be Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 13 Sep 2024 14:36:54 +0100 Subject: [PATCH 105/152] Add missing `"type": "object"` to RaiseIntentResultErrorResponsePayload --- schemas/api/raiseIntentResultResponse.schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/schemas/api/raiseIntentResultResponse.schema.json b/schemas/api/raiseIntentResultResponse.schema.json index f4f1419f3..4cde49972 100644 --- a/schemas/api/raiseIntentResultResponse.schema.json +++ b/schemas/api/raiseIntentResultResponse.schema.json @@ -49,6 +49,7 @@ }, "RaiseIntentResultErrorResponsePayload": { "title": "RaiseIntentResult Error Response Payload", + "type": "object", "properties": { "error": { "$ref": "common.schema.json#/$defs/ErrorMessages" From ce844ebde92e7418771f27ac9b8ea393c13f002d Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 13 Sep 2024 18:09:36 +0100 Subject: [PATCH 106/152] completing final section of DACP 'Routing, Registering Listeners & Multiplexing' --- docs/api/specs/desktopAgentCommunicationProtocol.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 36ac16e99..46f59ce1b 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -75,11 +75,15 @@ All messages defined in the DACP follow a common structure: ### Routing, Registering Listeners & Multiplexing -//messages SHOULD be routed rather than implement over a bus +The design of the Desktop Agent Communication Protocol is guided by the following sentence from the introduction to the Desktop Agent overview: -//messages are sent to register and unregister listeners so that DAs can send only relevant messages to app for security +> A Desktop Agent is a desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. -//DAs only need to send one event message, even when there are multiple listeners, getAgent() should fire all relevant listeners. +Hence, that design is based on the assumption that all messaging between applications passes through an entity that acts as the 'Desktop Agent' and routes those messages on to the appropriate recipients (for example a context message broadcast by an app to a channel is routed onto other apps that have added a listener to that channel, or an intent and context pair raised by an application is routed to another app chosen to resolve that intent). While implementations based on a shared bus are possible, they have not been specifically considered in the design of the DACP messages. + +Further, the design of the DACP is based on the assumption that applications will interact with an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface, with the DACP used behind the scenes to support communication between the implementation of that interface and an entity acting as the Desktop Agent which is running in another process or location, necessitating the use of a 'wire protocol' for communication. For example, [Browser-Resident Desktop Agent](./browserResidentDesktopAgents) implementations use the [FDC3 Web Communication Protocol (WCP)](./webConnectionProtocol.md) to connect a 'Desktop Agent Proxy', provided by the `getAgent()` implementation in the [`@finos/fdc3` npm module], and a Desktop Agent running in another frame or window which is communicated with via the DACP. + +As a Desktop Agent is expected to act as a router for messages sent through the Desktop Agent API, the DACP provides message exchanges for the registration and unregistration of listeners for particular message types (e.g. events, contexts broadcast on user channels, contexts broadcast on other channel types, raised intents etc.). In most cases, apps can register multiple listeners for the same messages (often filtered for different context or event types). However, where multiple listeners are present, only a single DACP message should be sent representing the action taken in the FDC3 API (e.g. broadcasting a message to a channel) and any multiplexing to multiple listeners should be applied at the receiving end. For example, when working with the WCP, this should be handled by the Desktop Agent Proxy implementation provided by the `getAgent()` implementation. ## Message Definitions Supporting FDC3 API calls From f4b44a16927e097120beb598e03f08590d905f3f Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 16 Sep 2024 10:29:26 +0100 Subject: [PATCH 107/152] Simplify heart message fields and use the eventUuid to link response to event --- schemas/api/heartbeatAcknowledgment.schema.json | 9 ++++----- schemas/api/heartbeatEvent.schema.json | 11 ++--------- src/api/BrowserTypes.ts | 11 +++-------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/schemas/api/heartbeatAcknowledgment.schema.json b/schemas/api/heartbeatAcknowledgment.schema.json index b5ce3f5f8..b1f0d24ac 100644 --- a/schemas/api/heartbeatAcknowledgment.schema.json +++ b/schemas/api/heartbeatAcknowledgment.schema.json @@ -31,16 +31,15 @@ "title": "heartbeatAcknowledgement Request Payload", "type": "object", "properties": { - "timestamp": { - "title": "Timestamp", - "description": "The timestamp of the heartbeatEvent that is being acknowledged.", + "heartbeatEventUuid": { + "title": "Heartbeat Event Uuid", "type": "string", - "format": "date-time" + "description": "The eventUuid value of the HeartbeatEvent that the acknowledgement being sent relates to." } }, "additionalProperties": false, "required": [ - "timestamp" + "heartbeatEventUuid" ] } } diff --git a/schemas/api/heartbeatEvent.schema.json b/schemas/api/heartbeatEvent.schema.json index e88ef7f3c..e1bdd9704 100644 --- a/schemas/api/heartbeatEvent.schema.json +++ b/schemas/api/heartbeatEvent.schema.json @@ -30,16 +30,9 @@ "HeartbeatEventPayload": { "title": "heartbeat Event Payload", "type": "object", - "properties": { - "timestamp": { - "title": "Timestamp", - "description": "The time at which the heartbeat event was sent, which should be quoted in the response", - "type": "string", - "format": "date-time" - } - }, + "properties": {}, "additionalProperties": false, - "required": ["timestamp"] + "required": [] } } } \ No newline at end of file diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index e268fdaf1..bc9016a65 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1997,9 +1997,9 @@ export interface HeartbeatAcknowledgementRequest { */ export interface HeartbeatAcknowledgementRequestPayload { /** - * The timestamp of the heartbeatEvent that is being acknowledged. + * The eventUuid value of the HeartbeatEvent that the acknowledgement being sent relates to. */ - timestamp: Date; + heartbeatEventUuid: string; } /** @@ -2034,10 +2034,6 @@ export interface HeartbeatEvent { * The message payload contains details of the event that the app is being notified about. */ export interface HeartbeatEventPayload { - /** - * The time at which the heartbeat event was sent, which should be quoted in the response - */ - timestamp: Date; } /** @@ -5027,7 +5023,7 @@ const typeMap: any = { { json: "type", js: "type", typ: r("HeartbeatAcknowledgementRequestType") }, ], false), "HeartbeatAcknowledgementRequestPayload": o([ - { json: "timestamp", js: "timestamp", typ: Date }, + { json: "heartbeatEventUuid", js: "heartbeatEventUuid", typ: "" }, ], false), "HeartbeatEvent": o([ { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, @@ -5035,7 +5031,6 @@ const typeMap: any = { { json: "type", js: "type", typ: r("HeartbeatEventType") }, ], false), "HeartbeatEventPayload": o([ - { json: "timestamp", js: "timestamp", typ: Date }, ], false), "IframeChannels": o([ { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, From 07469daa5556d3968ad635fde59830cb65753156 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 16 Sep 2024 10:32:20 +0100 Subject: [PATCH 108/152] minor formatting in browser spec --- docs/api/specs/browserResidentDesktopAgents.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 7955b560d..c3d8617fd 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -191,7 +191,7 @@ For more details on the connection process, please see the documentation for the ### Disconnects -DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. +DAs are responsible for tracking when app windows close or navigates, which is necessary to provide accurate responses to the `findIntent`, `findIntentsByContext` & `findInstances` API calls, and to correctly resolve raised intents. :::info @@ -205,7 +205,7 @@ Checking whether an application has closed may be achieved by a number of approa - However, it should be noted that the `closed` will be `false` if the window has navigated same-domain, but is no longer an FDC3 app or has become a different FDC3 app. Hence, checking the `closed` property will not catch all cases. - If an equivalent `WindowProxy` object (`WindowProxy` objects can be compared with `==` and will be equivalent if they represent the same window) is received from a different application the DA should consider the original application using that `WindowProxy` to have closed. - By receiving a `WCP6Goodbye` message from the application when it is closing. The `getAgent()` implementation automates the sending of this message via the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html). Specifically, the `getAgent()` implementation MUST attempt to detect windows closing by listening for the `pagehide` event and considering a window to be closed if the event's `persisted` property is `false`. - - Note that the pagehide event may not fire if the window's render thread crashes or is closed while 'frozen'. + - Note that the `pagehide` event may not fire if the window's render thread crashes or is closed while 'frozen'. - By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. From 32acca72f7c6c5cf105a2c4253044e4368e67d68 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 16 Sep 2024 14:22:44 +0100 Subject: [PATCH 109/152] Update docs/api/specs/browserResidentDesktopAgents.md Co-authored-by: Julianna Langston <74684272+julianna-ciq@users.noreply.github.com> --- docs/api/specs/browserResidentDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index c3d8617fd..c44a24e7d 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -229,7 +229,7 @@ The hidden iframe url can be provided in two ways: Channel Selector and Intent Resolver user-interfaces are normally provided by Desktop Agents to applications. However, when running in a web browser a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()` and it may be challenging to display a secondary window over the application when needed (due to pop-up blocking and user preferences). -The `getAgent()` implementation can facilitate the injection and management of iframes in an application window. An app may use the optional `channelSelector` and `intentResolver` parameters to the `getAgent()` to indicate whether or not they need these interfaces (for example they may not raise intents or may provide their own resolver UI based on the DA API's `findIntent` or `findIntentsForContext` functions so they don't need a DA-provided interface). These parameters are forwarded onto the Desktop Agent in the `WCP1Hello` connection message. +The `getAgent()` implementation can facilitate the injection and management of iframes in an application window. An app may provide the optional `channelSelector` and `intentResolver` parameters to the `getAgent()` to indicate whether or not they need these interfaces. For example, the apps may not raise intents. Some apps may also resolve intents internally by leveraging the Desktop Agent's `findIntent` or `findIntentsForContext` API functions. In these situations, the apps won't need a DA-provided interface. Once an app calls `getAgent()`, the parameters that the app provides are forwarded onto the Desktop Agent in the `WCP1Hello` connection message. Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is be able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. From 96cfe6d46628053eced24a1e0955422ab1818db6 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 16 Sep 2024 14:23:37 +0100 Subject: [PATCH 110/152] Update docs/api/specs/browserResidentDesktopAgents.md Co-authored-by: Julianna Langston <74684272+julianna-ciq@users.noreply.github.com> --- docs/api/specs/browserResidentDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index c44a24e7d..d7d5e6bde 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -227,7 +227,7 @@ The hidden iframe url can be provided in two ways: ## Channel Selector and Intent Resolver User Interfaces -Channel Selector and Intent Resolver user-interfaces are normally provided by Desktop Agents to applications. However, when running in a web browser a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()` and it may be challenging to display a secondary window over the application when needed (due to pop-up blocking and user preferences). +Channel Selector and Intent Resolver user-interfaces are normally provided by Desktop Agents to applications. However, when running in a web browser, a DA may not have the ability to present a channel selector in a window that has been opened with `window.open()`, and it may be challenging to display a secondary window over the application when needed (due to pop-up blocking and user preferences). The `getAgent()` implementation can facilitate the injection and management of iframes in an application window. An app may provide the optional `channelSelector` and `intentResolver` parameters to the `getAgent()` to indicate whether or not they need these interfaces. For example, the apps may not raise intents. Some apps may also resolve intents internally by leveraging the Desktop Agent's `findIntent` or `findIntentsForContext` API functions. In these situations, the apps won't need a DA-provided interface. Once an app calls `getAgent()`, the parameters that the app provides are forwarded onto the Desktop Agent in the `WCP1Hello` connection message. From 743ede1fdbe660dfec01df5c528ffc272cc434cf Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 18 Sep 2024 17:25:21 +0100 Subject: [PATCH 111/152] Making Robs requested change to WCP step metadata --- schemas/api/WCP1Hello.schema.json | 6 +- schemas/api/WCP2LoadUrl.schema.json | 6 +- schemas/api/WCP3Handshake.schema.json | 6 +- .../api/WCP4ValidateAppIdentity.schema.json | 6 +- ...idateAppIdentityFailedResponse.schema.json | 7 +- ...CP5ValidateAppIdentityResponse.schema.json | 7 +- schemas/api/WCP6Goodbye.schema.json | 14 ++- schemas/api/WCPConnectionStep.schema.json | 24 ++++- src/api/BrowserTypes.ts | 90 ++++++++++++++----- 9 files changed, 127 insertions(+), 39 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 7698986f7..66a85f357 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -54,7 +54,11 @@ }, "required": ["identityUrl","actualUrl","fdc3Version"] }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", "payload", "meta"], "additionalProperties": false diff --git a/schemas/api/WCP2LoadUrl.schema.json b/schemas/api/WCP2LoadUrl.schema.json index 420303faf..e7c9a5ac9 100644 --- a/schemas/api/WCP2LoadUrl.schema.json +++ b/schemas/api/WCP2LoadUrl.schema.json @@ -35,7 +35,11 @@ "iframeUrl" ] }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json index cbcedb5e8..02c25499a 100644 --- a/schemas/api/WCP3Handshake.schema.json +++ b/schemas/api/WCP3Handshake.schema.json @@ -63,7 +63,11 @@ "channelSelectorUrl" ] }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", diff --git a/schemas/api/WCP4ValidateAppIdentity.schema.json b/schemas/api/WCP4ValidateAppIdentity.schema.json index 9ef23a20a..244b196d0 100644 --- a/schemas/api/WCP4ValidateAppIdentity.schema.json +++ b/schemas/api/WCP4ValidateAppIdentity.schema.json @@ -52,7 +52,11 @@ "identityUrl", "actualUrl" ] }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", diff --git a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json index df1b85925..b373e894a 100644 --- a/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityFailedResponse.schema.json @@ -31,10 +31,15 @@ }, "additionalProperties": false }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", + "payload", "meta" ], "additionalProperties": false diff --git a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json index f2dd3f8cf..b448995c9 100644 --- a/schemas/api/WCP5ValidateAppIdentityResponse.schema.json +++ b/schemas/api/WCP5ValidateAppIdentityResponse.schema.json @@ -53,10 +53,15 @@ "implementationMetadata" ] }, - "meta": true + "meta": { + "title": "Connection Step Metadata", + "description": "Metadata for this connection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/ConnectionStepMeta" + } }, "required": [ "type", + "payload", "meta" ], "additionalProperties": false diff --git a/schemas/api/WCP6Goodbye.schema.json b/schemas/api/WCP6Goodbye.schema.json index 39f1a4d34..25ae9319a 100644 --- a/schemas/api/WCP6Goodbye.schema.json +++ b/schemas/api/WCP6Goodbye.schema.json @@ -20,15 +20,13 @@ "title": "WCP6Goodbye Message Type", "const": "WCP6Goodbye" }, - "payload": { - "title": "WCP6Goodbye Payload", - "type": "object", - "properties": { }, - "required": [] - }, - "meta": true + "meta": { + "title": "Disconnect Metadata", + "description": "Metadata for a disconnection step message", + "$ref": "WCPConnectionStep.schema.json#/$defs/DisconnectStepMeta" + } }, - "required": [ "type", "payload", "meta"], + "required": [ "type", "meta"], "additionalProperties": false } } diff --git a/schemas/api/WCPConnectionStep.schema.json b/schemas/api/WCPConnectionStep.schema.json index 1be4e5a10..11fb91463 100644 --- a/schemas/api/WCPConnectionStep.schema.json +++ b/schemas/api/WCPConnectionStep.schema.json @@ -26,12 +26,18 @@ "additionalProperties": true }, "meta": { - "$ref": "#/$defs/ConnectionStepMeta" + "oneOf": [ + { + "$ref": "#/$defs/DisconnectStepMeta" + }, + { + "$ref": "#/$defs/ConnectionStepMeta" + } + ] } }, "required": [ "type", - "payload", "meta" ], "additionalProperties": false, @@ -53,6 +59,20 @@ "connectionAttemptUuid" ], "additionalProperties": false + }, + "DisconnectStepMeta": { + "title": "Disconnect Metadata", + "description": "Metadata for a disconnection step message", + "type": "object", + "properties": { + "timestamp": { + "$ref": "common.schema.json#/$defs/Timestamp" + } + }, + "required": [ + "timestamp" + ], + "additionalProperties": false } } } \ No newline at end of file diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index bc9016a65..088ee3c27 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -3512,7 +3512,10 @@ export interface RaiseIntentResultResponsePayload { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol1Hello { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3525,8 +3528,10 @@ export interface WebConnectionProtocol1Hello { /** * Metadata for this connection step message + * + * Metadata for a disconnection step message */ -export interface ConnectionStepMetadata { +export interface WebConnectionProtocol1HelloMeta { connectionAttemptUuid: string; timestamp: Date; } @@ -3575,7 +3580,10 @@ export interface WebConnectionProtocol1HelloPayload { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol2LoadURL { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3611,7 +3619,10 @@ export interface WebConnectionProtocol2LoadURLPayload { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol3Handshake { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3655,7 +3666,10 @@ export interface WebConnectionProtocol3HandshakePayload { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol4ValidateAppIdentity { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3702,7 +3716,10 @@ export interface WebConnectionProtocol4ValidateAppIdentityPayload { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol5ValidateAppIdentityFailedResponse { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3731,7 +3748,10 @@ export interface WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol5ValidateAppIdentitySuccessResponse { - meta: ConnectionStepMetadata; + /** + * Metadata for this connection step message + */ + meta: WebConnectionProtocol1HelloMeta; /** * The message payload, containing data pertaining to this connection step. */ @@ -3780,17 +3800,25 @@ export interface WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocol6Goodbye { - meta: ConnectionStepMetadata; /** - * The message payload, containing data pertaining to this connection step. + * Metadata for a disconnection step message */ - payload: { [key: string]: any }; + meta: WebConnectionProtocol6GoodbyeMeta; /** * Identifies the type of the connection step message. */ type: "WCP6Goodbye"; } +/** + * Metadata for a disconnection step message + * + * Metadata for this connection step message + */ +export interface WebConnectionProtocol6GoodbyeMeta { + timestamp: Date; +} + /** * Identifies the type of the connection step message. */ @@ -3800,17 +3828,27 @@ export interface WebConnectionProtocol6Goodbye { * browser window. Used for messages sent in either direction. */ export interface WebConnectionProtocolMessage { - meta: ConnectionStepMetadata; + meta: WebConnectionProtocolMessageMeta; /** * The message payload, containing data pertaining to this connection step. */ - payload: { [key: string]: any }; + payload?: { [key: string]: any }; /** * Identifies the type of the connection step message. */ type: ConnectionStepMessageType; } +/** + * Metadata for a disconnection step message + * + * Metadata for this connection step message + */ +export interface WebConnectionProtocolMessageMeta { + timestamp: Date; + connectionAttemptUuid?: string; +} + /** * Identifies the type of the connection step message. */ @@ -5343,11 +5381,11 @@ const typeMap: any = { { json: "intentResult", js: "intentResult", typ: u(undefined, r("IntentResult")) }, ], false), "WebConnectionProtocol1Hello": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol1HelloPayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol1HelloType") }, ], false), - "ConnectionStepMetadata": o([ + "WebConnectionProtocol1HelloMeta": o([ { json: "connectionAttemptUuid", js: "connectionAttemptUuid", typ: "" }, { json: "timestamp", js: "timestamp", typ: Date }, ], false), @@ -5359,7 +5397,7 @@ const typeMap: any = { { json: "intentResolver", js: "intentResolver", typ: u(undefined, true) }, ], "any"), "WebConnectionProtocol2LoadURL": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol2LoadURLPayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol2LoadURLType") }, ], false), @@ -5367,7 +5405,7 @@ const typeMap: any = { { json: "iframeUrl", js: "iframeUrl", typ: "" }, ], "any"), "WebConnectionProtocol3Handshake": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol3HandshakePayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol3HandshakeType") }, ], false), @@ -5377,7 +5415,7 @@ const typeMap: any = { { json: "intentResolverUrl", js: "intentResolverUrl", typ: u(true, "") }, ], false), "WebConnectionProtocol4ValidateAppIdentity": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol4ValidateAppIdentityPayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol4ValidateAppIdentityType") }, ], false), @@ -5388,7 +5426,7 @@ const typeMap: any = { { json: "instanceUuid", js: "instanceUuid", typ: u(undefined, "") }, ], false), "WebConnectionProtocol5ValidateAppIdentityFailedResponse": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol5ValidateAppIdentityFailedResponsePayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol5ValidateAppIdentityFailedResponseType") }, ], false), @@ -5396,7 +5434,7 @@ const typeMap: any = { { json: "message", js: "message", typ: u(undefined, "") }, ], false), "WebConnectionProtocol5ValidateAppIdentitySuccessResponse": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol1HelloMeta") }, { json: "payload", js: "payload", typ: r("WebConnectionProtocol5ValidateAppIdentitySuccessResponsePayload") }, { json: "type", js: "type", typ: r("WebConnectionProtocol5ValidateAppIdentitySuccessResponseType") }, ], false), @@ -5407,15 +5445,21 @@ const typeMap: any = { { json: "instanceUuid", js: "instanceUuid", typ: "" }, ], false), "WebConnectionProtocol6Goodbye": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: m("any") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocol6GoodbyeMeta") }, { json: "type", js: "type", typ: r("WebConnectionProtocol6GoodbyeType") }, ], false), + "WebConnectionProtocol6GoodbyeMeta": o([ + { json: "timestamp", js: "timestamp", typ: Date }, + ], false), "WebConnectionProtocolMessage": o([ - { json: "meta", js: "meta", typ: r("ConnectionStepMetadata") }, - { json: "payload", js: "payload", typ: m("any") }, + { json: "meta", js: "meta", typ: r("WebConnectionProtocolMessageMeta") }, + { json: "payload", js: "payload", typ: u(undefined, m("any")) }, { json: "type", js: "type", typ: r("ConnectionStepMessageType") }, ], false), + "WebConnectionProtocolMessageMeta": o([ + { json: "timestamp", js: "timestamp", typ: Date }, + { json: "connectionAttemptUuid", js: "connectionAttemptUuid", typ: u(undefined, "") }, + ], false), "AddContextListenerRequestType": [ "addContextListenerRequest", ], From 169e6c9fcfdddcd2ca23121f704ded0b182b1aab Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 18 Sep 2024 17:34:56 +0100 Subject: [PATCH 112/152] Fix references to Page Lifecycle API --- docs/api/specs/webConnectionProtocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index ab1eef269..fa40d6bc4 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -290,7 +290,7 @@ Resolve the `getAgent()` promise with an object containing either a `DesktopAgen Desktop Agent Preload interfaces, as used in container-based Desktop Agent implementations, are usually able to track the lifecycle and current URL of windows that host web apps in their scope. Hence, this is currently no requirement nor means for an app to indicate that it is closing, rather it is the responsibility of the Desktop Agent to update its internal state when an app closes or changes identity. -However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the the HTML Standard's [Page Life Cycle API](https://wicg.github.io/page-lifecycle/spec.html) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. +However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the HTML Standard's [Page Life Cycle API](https://html.spec.whatwg.org/multipage/document-lifecycle.html#document-lifecycle) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event), [Chrome for Developers](https://developer.chrome.com/docs/web-platform/page-lifecycle-api#developer-recommendations-for-each-state)) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection may also be used, these are described in the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects) and [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). From ef5fc762d221764234c5055037123df7d2101576 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 19 Sep 2024 15:57:49 +0100 Subject: [PATCH 113/152] Adding detail about initial styling of interface iframes --- docs/api/specs/browserResidentDesktopAgents.md | 12 ++++++++++++ schemas/api/iFrameHello.schema.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index d7d5e6bde..7bf489236 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -233,6 +233,18 @@ The `getAgent()` implementation can facilitate the injection and management of i Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is be able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. +User interface iframes are initially injected into the application window with CSS that prevents their display: + +```css +{ + width: "0"; + height: "0"; + position: "fixed"; +} +``` + +Implementations of the UIs may then indicate a limited set of CSS to apply to their frame in the initial `iFrameHello` 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. + 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. 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`. diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/iFrameHello.schema.json index 78f736492..2f88862d8 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/iFrameHello.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://fdc3.finos.org/schemas/next/api/iframeHello.schema.json", "title": "iframe Hello", - "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate, containing initial CSS to set on the iframe and including an appended `MessagePort` to be used for further communication.", + "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate, containing initial CSS to set on the iframe, and including an appended `MessagePort` to be used for further communication.", "type": "object", "allOf": [ { From d4e45c4355cd16ded7af15f51bb637128ab06f3d Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 19 Sep 2024 17:53:47 +0100 Subject: [PATCH 114/152] Formatting and other corrections from review meeting 20240919 --- docs/api/ref/GetAgent.md | 2 +- docs/api/specs/browserResidentDesktopAgents.md | 15 +++++++-------- .../specs/desktopAgentCommunicationProtocol.md | 3 +-- docs/api/specs/webConnectionProtocol.md | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 3f292990b..5169840e6 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -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. +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). The definition of the `getAgent()` function is as follows: diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 7bf489236..8dcf4fc11 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -50,9 +50,9 @@ When an app runs `getAgent()`, it checks for the existence of `window.parent`, ` Hence, apps may be launched: -1) By creating iframes in a DA Window -2) By calling `window.open` from a DA Window -3) By creating iframes in a window that was opened from a DA Window +1. By creating iframes in a DA Window +2. By calling `window.open` from a DA Window +3. By creating iframes in a window that was opened from a DA Window and the Desktop Agent application will be found in a 'parent' of the application frame. @@ -62,9 +62,9 @@ Browser Resident DAs MUST call `window.addEventListener("message",...)` to recei Upon receiving an incoming [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) the Desktop Agent MUST either: -1) Respond with a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). +1. Respond with a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) to the iframe. -2) Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. +2. Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message from the application. - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel` to the message. @@ -221,9 +221,8 @@ As described above, DA providers can leverage hidden iframes to establish a comm The hidden iframe url can be provided in two ways: -1) By a Parent window - This allows DAs to redirect communications to via a hidden iframe that loads a known URL. The main benefit of this approach is that it can allow a system to continue to operate even if the parent window is closed. - -2) By a `failover` function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can create a hidden iframe and return a reference to it (a `WindowProxy`) to initiate communication with via the WCP and DACP in the same way as we do with a parent window. Alternatively, a `DesktopAgent` implementation maybe loaded directly and returned from the failover function, which `getAgent()` will pass-through. +1. By a Parent window - This allows DAs to redirect communications to via a hidden iframe that loads a known URL. The main benefit of this approach is that it can allow a system to continue to operate even if the parent window is closed. +2. By a `failover` function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can create a hidden iframe and return a reference to it (a `WindowProxy`) to initiate communication with via the WCP and DACP in the same way as we do with a parent window. Alternatively, a `DesktopAgent` implementation maybe loaded directly and returned from the failover function, which `getAgent()` will pass-through. ## Channel Selector and Intent Resolver User Interfaces diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 46f59ce1b..e7f002d0b 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -316,7 +316,6 @@ title: Intent resolution with injected Intent Resolver iframe --- sequenceDiagram AppA ->> DesktopAgent: raiseIntentRequest - DesktopAgent ->> AppB: intentEvent DesktopAgent ->> AppA: raiseIntentResponse Note left of DesktopAgent: raiseIntentResponse includes a
RaiseIntentNeedsResolutionResponsePayload
containing an AppIntent break when AppIntent return with multiple options @@ -340,11 +339,11 @@ title: Intent resolution with Desktop Agent provided Intent Resolver --- sequenceDiagram AppA ->> DesktopAgent: raiseIntentRequest - DesktopAgent ->> AppB: intentEvent break DA determines there are multiple options DesktopAgent-->AppA: Desktop Agent displays an
IntentResolver UI AppA-->DesktopAgent: User picks an option end + DesktopAgent ->> AppB: intentEvent DesktopAgent ->> AppA: raiseIntentResponse Note left of DesktopAgent: DesktopAgent responds
to the original
raiseIntentRequest message with
a RaiseIntentSuccessResponsePayload AppB ->> DesktopAgent: intentResultRequest diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index fa40d6bc4..712c08510 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -46,7 +46,7 @@ There are a number of messages defined as part of the Web Connection Protocol. D TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import {BrowserTypes} from '@finos.fdc3'; +import {BrowserTypes} from '@finos/fdc3'; ``` ::: @@ -188,7 +188,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp timestamp: "2024-09-09T11:44:39+00:00" } }; - candidate.postMessage(hello, { targetOrigin: * };) + candidate.postMessage(hello, { targetOrigin: "*" }); ``` Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. From 91f3e5d165215d0fbb211f0caa761e56b5464299 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 19 Sep 2024 17:58:55 +0100 Subject: [PATCH 115/152] further clarifying iframe positioning/position fixed --- docs/api/specs/browserResidentDesktopAgents.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 8dcf4fc11..8e5b32ac0 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -242,7 +242,9 @@ User interface iframes are initially injected into the application window with C } ``` -Implementations of the UIs may then indicate a limited set of CSS to apply to their frame in the initial `iFrameHello` 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. +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. 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. From 3e8d0e75ca2a6c8d6ab27c05d6d92d87c7164dee Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 19 Sep 2024 20:07:28 +0100 Subject: [PATCH 116/152] Correcting minor spelling errors --- docs/api/specs/desktopAgentCommunicationProtocol.md | 4 ++-- docs/api/specs/webConnectionProtocol.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index e7f002d0b..aff477daa 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -83,7 +83,7 @@ Hence, that design is based on the assumption that all messaging between applica Further, the design of the DACP is based on the assumption that applications will interact with an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface, with the DACP used behind the scenes to support communication between the implementation of that interface and an entity acting as the Desktop Agent which is running in another process or location, necessitating the use of a 'wire protocol' for communication. For example, [Browser-Resident Desktop Agent](./browserResidentDesktopAgents) implementations use the [FDC3 Web Communication Protocol (WCP)](./webConnectionProtocol.md) to connect a 'Desktop Agent Proxy', provided by the `getAgent()` implementation in the [`@finos/fdc3` npm module], and a Desktop Agent running in another frame or window which is communicated with via the DACP. -As a Desktop Agent is expected to act as a router for messages sent through the Desktop Agent API, the DACP provides message exchanges for the registration and unregistration of listeners for particular message types (e.g. events, contexts broadcast on user channels, contexts broadcast on other channel types, raised intents etc.). In most cases, apps can register multiple listeners for the same messages (often filtered for different context or event types). However, where multiple listeners are present, only a single DACP message should be sent representing the action taken in the FDC3 API (e.g. broadcasting a message to a channel) and any multiplexing to multiple listeners should be applied at the receiving end. For example, when working with the WCP, this should be handled by the Desktop Agent Proxy implementation provided by the `getAgent()` implementation. +As a Desktop Agent is expected to act as a router for messages sent through the Desktop Agent API, the DACP provides message exchanges for the registration and un-registration of listeners for particular message types (e.g. events, contexts broadcast on user channels, contexts broadcast on other channel types, raised intents etc.). In most cases, apps can register multiple listeners for the same messages (often filtered for different context or event types). However, where multiple listeners are present, only a single DACP message should be sent representing the action taken in the FDC3 API (e.g. broadcasting a message to a channel) and any multiplexing to multiple listeners should be applied at the receiving end. For example, when working with the WCP, this should be handled by the Desktop Agent Proxy implementation provided by the `getAgent()` implementation. ## Message Definitions Supporting FDC3 API calls @@ -445,4 +445,4 @@ Messages are also provided that are specific to each interface type provided by Messages specific to Intent Resolver user interfaces: - [`iFrameResolve`](https://fdc3.finos.org/schemas/next/api/iFrameResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. -- [`iFrameResolveAction`](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementaiton after a resolution option is selected. +- [`iFrameResolveAction`](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 712c08510..f1880a857 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -72,7 +72,7 @@ A value for `meta.connectionAttemptUuid` should be generated as a version 4 UUID Messages defined as part of the Web Connection Protocol, which will be referenced later in this document, these are: -- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) +- [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) - [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) - [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) - [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) From 2e5da9805cf1f68254ff983495558436ba78ace0 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 10:31:37 +0100 Subject: [PATCH 117/152] Standardize spacing on javascript import examples --- docs/api/specs/desktopAgentCommunicationProtocol.md | 2 +- docs/api/specs/webConnectionProtocol.md | 2 +- docs/api/supported-platforms.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index aff477daa..efa78f10e 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -23,7 +23,7 @@ DACP messages are defined in [JSON Schema](https://json-schema.org/) in the [FDC TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import {BrowserTypes} from '@finos/fdc3'; +import { BrowserTypes } from '@finos/fdc3'; ``` ::: diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index f1880a857..2b6b1555c 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -46,7 +46,7 @@ There are a number of messages defined as part of the Web Connection Protocol. D TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import {BrowserTypes} from '@finos/fdc3'; +import { BrowserTypes } from '@finos/fdc3'; ``` ::: diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index a2b5c94cb..f7c909ba2 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -113,7 +113,7 @@ await sendData(fdc3); The npm package provides a wrapper around FDC3, allowing you to use it with ES6 import syntax: ```javascript -import {raiseIntent} from '@finos/fdc3'; +import { raiseIntent } from '@finos/fdc3'; await raiseIntent('ViewAnalysis', { type: 'fdc3.instrument', From 81a35186b665e802cb1d03ce2d18e08bff7a8fd7 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 10:46:15 +0100 Subject: [PATCH 118/152] typo fix in GetAgent --- docs/api/ref/GetAgent.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 5169840e6..7b8d99d13 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -74,19 +74,18 @@ A small number of arguments are accepted that can affect the behavior of `getAge * * @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). If not set will - * default to true. MAY be ignored by Desktop Agent Preload (container) - * implementations. + * 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 - * needs access to an intent resolver UI (i.e. because it supports raising on or - * more intents and and does not implement its own UI for selecting target apps. - * If not set will default to true. MAY be ignored by Desktop Agent Preload - * (container) implementations. + * 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) + * 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 + * 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 From 5af5d06d25bb5d34d34b0d2a8232b1bf94a41c21 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 11:20:13 +0100 Subject: [PATCH 119/152] typo: alloted --- docs/agent-bridging/spec.md | 2 +- docs/api/ref/Errors.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/agent-bridging/spec.md b/docs/agent-bridging/spec.md index 009afe593..09e6e54fd 100644 --- a/docs/agent-bridging/spec.md +++ b/docs/agent-bridging/spec.md @@ -836,7 +836,7 @@ And an error enumeration is created for errors related to bridging that may occu ```typescript enum BridgingError { /** Returned if a Desktop Agent did not return a response, via Desktop Agent Bridging, - * within the alloted timeout. */ + * within the allotted timeout. */ ResponseTimedOut = 'ResponseToBridgeTimedOut', /** Returned if a Desktop Agent that has been targeted by a particular request has * been disconnected from the Bridge before a response has been received from it. */ diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 465c17f41..7a4a829e6 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -387,7 +387,7 @@ public static class ResultError ```ts enum BridgingError { /** @experimental Returned if a Desktop Agent did not return a response, via - * Desktop Agent Bridging, within the alloted timeout. */ + * Desktop Agent Bridging, within the allotted timeout. */ ResponseTimedOut = 'ResponseToBridgeTimedOut', /** @experimental Returned if a Desktop Agent that has been targeted by a * particular request has been disconnected from the Bridge before a From 4b43047f7ac0c843b1bb57d3a1e30762e30b402d Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 12:14:17 +0100 Subject: [PATCH 120/152] typo corrections --- docs/api/specs/browserResidentDesktopAgents.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 8e5b32ac0..dfe2579c8 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -209,7 +209,7 @@ Checking whether an application has closed may be achieved by a number of approa - By polling the application for responses via the `heartbeatEvent` and `heartbeatAcknowledgement` messages provided in the [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). These message may be used for both periodic and on-demand polling by DA implementations. On-demand polling could, for example, be used to check that all instances returned in a findIntent response or displayed in an intent resolver are still alive. - Desktop Agents MAY determine their own timeout, or support configuration, to be used for considering an application to have closed as this may be affected by the implementation details of app and DAs. -Finally, Desktop Agents SHOULD retain instance details for applications that have closed as they may appear to close during navigation events, or may navigate away and then navigate back. By retaining the instance data (`instanceId`, `instanceUuid` and `WindowProxy`) the same instance identity can be maintained or reissued. There is no standard length of time that such details should be retained, hance, Desktop Agents MAY determine for themselves how long to retain instance details for closed instances. +Finally, Desktop Agents SHOULD retain instance details for applications that have closed as they may appear to close during navigation events, or may navigate away and then navigate back. By retaining the instance data (`instanceId`, `instanceUuid` and `WindowProxy`) the same instance identity can be maintained or reissued. There is no standard length of time that such details should be retained, hence, Desktop Agents MAY determine for themselves how long to retain instance details for closed instances. ## Responding to app communications with Desktop Agent Communication Protocol (DACP) @@ -217,12 +217,12 @@ After validating an application's identity and any instance identity to be reuse ## Implementing DAs in hidden iframes -As described above, DA providers can leverage hidden iframes to establish a communication mechanism that is independent of a Parent window. This approach allows apps to connect to a DA even when they were not opened by that DA. +As described above, DA providers can leverage hidden iframes to establish a communication mechanism that is independent of a parent window or frame. This approach allows apps to reconnect to a DA even when the parent window or frame has closed, or to connect a DA they've started themselves via a `failover` function. -The hidden iframe url can be provided in two ways: +The hidden iframe URL can be provided in two ways: -1. By a Parent window - This allows DAs to redirect communications to via a hidden iframe that loads a known URL. The main benefit of this approach is that it can allow a system to continue to operate even if the parent window is closed. -2. By a `failover` function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can create a hidden iframe and return a reference to it (a `WindowProxy`) to initiate communication with via the WCP and DACP in the same way as we do with a parent window. Alternatively, a `DesktopAgent` implementation maybe loaded directly and returned from the failover function, which `getAgent()` will pass-through. +1. By a Parent window or frame - This allows DAs to handle communication via a hidden iframe that loads a known URL. The main benefit of this approach is that it can allow a system to continue to operate even if the parent window or frame is closed. +2. By a `failover` function - When no parent DA can be found (such as when a tab is opened directly by an end user) then a failover function can create a hidden iframe and return a reference to it (a `WindowProxy`) that is used to initiate communication via the WCP in the same way as we do with a parent window or frame. Alternatively, a `DesktopAgent` implementation may be loaded directly and returned from the `failover` function, which `getAgent()` will pass-through. ## Channel Selector and Intent Resolver User Interfaces @@ -230,7 +230,7 @@ Channel Selector and Intent Resolver user-interfaces are normally provided by De The `getAgent()` implementation can facilitate the injection and management of iframes in an application window. An app may provide the optional `channelSelector` and `intentResolver` parameters to the `getAgent()` to indicate whether or not they need these interfaces. For example, the apps may not raise intents. Some apps may also resolve intents internally by leveraging the Desktop Agent's `findIntent` or `findIntentsForContext` API functions. In these situations, the apps won't need a DA-provided interface. Once an app calls `getAgent()`, the parameters that the app provides are forwarded onto the Desktop Agent in the `WCP1Hello` connection message. -Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is be able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. +Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. User interface iframes are initially injected into the application window with CSS that prevents their display: From a9ba6217208a8b1f00f154335907ae044172ca43 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 13:45:57 +0100 Subject: [PATCH 121/152] Typos in DACP --- docs/api/specs/desktopAgentCommunicationProtocol.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index efa78f10e..5fdfc4bbc 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -141,7 +141,7 @@ An additional request and response used to deliver an [`IntentResult`](../ref/Ty Please note this exchange (and the `IntentResolution.getResult()` API call) support `void` results from a raised intent and hence this message exchange should occur for all raised intents, including those that do not return a result. In such cases, the void intent result allows resolution of the `IntentResolution.getResult()` API call and indicates that the intent handler has finished running. -Request and response for removing the intent listener ([`Listener.unsubscribe()`](../ref/Types#listener)):: +Request and response for removing the intent listener ([`Listener.unsubscribe()`](../ref/Types#listener)): - [`intentListenerUnsubscribeRequest`](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeRequest.schema.json) - [`intentListenerUnsubscribeResponse`](https://fdc3.finos.org/schemas/next/api/intentListenerUnsubscribeResponse.schema.json) @@ -242,7 +242,7 @@ Request and response used to implement the [`getInfo()`](../ref/DesktopAgent#get #### `getOrCreateChannel()` -Request and response used to implement the [`getOrCreateChannel()`](../ref/DesktopAgent#getorcreatechannel] API call: +Request and response used to implement the [`getOrCreateChannel()`](../ref/DesktopAgent#getorcreatechannel) API call: - [`getOrCreateChannelRequest`](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelRequest.schema.json) - [`getOrCreateChannelResponse`](https://fdc3.finos.org/schemas/next/api/getOrCreateChannelResponse.schema.json) @@ -306,7 +306,7 @@ See [`addIntentListener`](#addintentlistener) above for details of the messages ::: -Where there are multiple options for resolving a raised intents, there are two possible versions of the resulting message exchanges. Which to use depends on whether the Desktop Agent uses an intent resolver user interface (or other suitable mechanism) that it controls, or one injected into the application (for example an iframe injected by a `getAgent()` implementation into an application window) to perform resolution. +Where there are multiple options for resolving a raised intent, there are two possible versions of the resulting message exchanges. Which to use depends on whether the Desktop Agent uses an intent resolver user interface (or other suitable mechanism) that it controls, or one injected into the application (for example an iframe injected by a `getAgent()` implementation into an application window) to perform resolution. When working with an injected interface, the Desktop Agent should respond with a `raiseIntentResponse` containing a `RaiseIntentNeedsResolutionResponsePayload`: From afb0991878885b313810fd5a6817af5feb016a99 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 Sep 2024 17:17:09 +0100 Subject: [PATCH 122/152] Clarifying open in the DACP based on feedback --- docs/api/specs/desktopAgentCommunicationProtocol.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 5fdfc4bbc..39ea23c1e 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -287,8 +287,17 @@ sequenceDiagram AppB ->> DesktopAgent: addContextListenerRequest DesktopAgent ->> AppB: addContextListenerResponse DesktopAgent ->> AppB: broadcastEvent + DesktopAgent ->> AppA: openResponse
(with AppIdentifier) ``` +However, if the app opened doesn't add a context listener within a timeout (defined by the Desktop Agent) then the `openResponse` should be sent with `AppTimeout` error from the [`OpenError`](../ref/Errors#openerror) enumeration. + +:::tip + +Desktop Agents MUST allow at least 15 seconds for an app to add a context listener before timing out (see [Desktop Agent API Standard Compliance](https://fdc3.finos.org/docs/next/api/spec#desktop-agent-api-standard-compliance) for more detail) and applications SHOULD add their listeners as soon as possible to keep the delay short (see the [addContextListener reference doc](https://fdc3.finos.org/docs/next/api/ref/DesktopAgent#addcontextlistener)). + +::: + #### `raiseIntent()` Request and response used to implement the [`raiseIntent()`](../ref/DesktopAgent#raiseintent) API call: From 8f79cdd5bddacf02cb0cccfeff356ff172ea3eea Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 11:59:36 +0100 Subject: [PATCH 123/152] Use double quotes in code examples --- docs/api/spec.md | 94 ++++++++++++------------- docs/api/specs/webConnectionProtocol.md | 8 +-- docs/api/supported-platforms.md | 12 ++-- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 57ecd93bd..76b8dae24 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -184,10 +184,10 @@ Since version 1.2 of the FDC3 Standard it may do so via the [`fdc3.getInfo()`](r ```ts import {compareVersionNumbers, versionIsAtLeast} from '@finos/fdc3'; -if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), '1.2')) { +if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), "1.2")) { await fdc3.raiseIntentForContext(context); } else { - await fdc3.raiseIntent('ViewChart', context); + await fdc3.raiseIntent("ViewChart", context); } ``` @@ -381,7 +381,7 @@ For example, to raise a specific intent: ```ts try { - const resolution = await fdc3.raiseIntent('StageOrder', context); + const resolution = await fdc3.raiseIntent("StageOrder", context); } catch (err){ ... } ``` @@ -440,7 +440,7 @@ Use metadata about the resolving app instance to target a further intent ```ts try { - const resolution = await fdc3.raiseIntent('StageOrder', context); + const resolution = await fdc3.raiseIntent("StageOrder", context); ... //some time later @@ -606,7 +606,7 @@ To find a User channel, one calls: ```ts // returns an array of channels const allChannels = await fdc3.getUserChannels(); -const redChannel = allChannels.find(c => c.id === 'red'); +const redChannel = allChannels.find(c => c.id === "red"); ```
@@ -656,75 +656,75 @@ Future versions of the FDC3 Standard may support connections between desktop age ```ts const recommendedChannels = [ { - id: 'fdc3.channel.1', - type: 'user', + id: "fdc3.channel.1", + type: "user", displayMetadata: { - name: 'Channel 1', - color: 'red', - glyph: '1', + name: "Channel 1", + color: "red", + glyph: "1", }, }, { - id: 'fdc3.channel.2', - type: 'user', + id: "fdc3.channel.2", + type: "user", displayMetadata: { - name: 'Channel 2', - color: 'orange', - glyph: '2', + name: "Channel 2", + color: "orange", + glyph: "2", }, }, { - id: 'fdc3.channel.3', - type: 'user', + id: "fdc3.channel.3", + type: "user", displayMetadata: { - name: 'Channel 3', - color: 'yellow', - glyph: '3', + name: "Channel 3", + color: "yellow", + glyph: "3", }, }, { - id: 'fdc3.channel.4', - type: 'user', + id: "fdc3.channel.4", + type: "user", displayMetadata: { - name: 'Channel 4', - color: 'green', - glyph: '4', + name: "Channel 4", + color: "green", + glyph: "4", }, }, { - id: 'fdc3.channel.5', - type: 'user', + id: "fdc3.channel.5", + type: "user", displayMetadata: { - name: 'Channel 5', - color: 'cyan', - glyph: '5', + name: "Channel 5", + color: "cyan", + glyph: "5", }, }, { - id: 'fdc3.channel.6', - type: 'user', + id: "fdc3.channel.6", + type: "user", displayMetadata: { - name: 'Channel 6', - color: 'blue', - glyph: '6', + name: "Channel 6", + color: "blue", + glyph: "6", }, }, { - id: 'fdc3.channel.7', - type: 'user', + id: "fdc3.channel.7", + type: "user", displayMetadata: { - name: 'Channel 7', - color: 'magenta', - glyph: '7', + name: "Channel 7", + color: "magenta", + glyph: "7", }, }, { - id: 'fdc3.channel.8', - type: 'user', + id: "fdc3.channel.8", + type: "user", displayMetadata: { - name: 'Channel 8', - color: 'purple', - glyph: '8', + name: "Channel 8", + color: "purple", + glyph: "8", }, }, ]; @@ -744,7 +744,7 @@ To get (or create) a [`Channel`](ref/Channel) reference, then interact with it: ```ts -const appChannel = await fdc3.getOrCreateChannel('my_custom_channel'); +const appChannel = await fdc3.getOrCreateChannel("my_custom_channel"); // get the current context of the channel const current = await appChannel.getCurrentContext(); // add a listener @@ -783,7 +783,7 @@ let joinedChannel = await fdc3.getCurrentChannel() const listener = await fdc3.addContextListener(null, context => { ... }); //retrieve an App channel and add a listener that is specific to that channel -const myChannel = await fdc3.getOrCreateChannel('my_custom_channel'); +const myChannel = await fdc3.getOrCreateChannel("my_custom_channel"); const myChannelListener = await myChannel.addContextListener(null, context => { ... }); fdc3.joinChannel('blue') diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 2b6b1555c..4e5cad0b8 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -122,24 +122,24 @@ const discoverPreloadDA = async (timeout): Promise => { clearTimeout(timeout); resolve(window.fdc3); } else { - reject('The `fdc3Ready` event fired, but `window.fdc3` Was not set!'); + reject("The `fdc3Ready` event fired, but `window.fdc3` Was not set!"); } }; // Setup a timeout to return a rejected promise const timeout = setTimeout( () => { //clear the event listener to ignore a late event - window.removeEventListener('fdc3Ready', listener); + window.removeEventListener("fdc3Ready", listener); if (window.fdc3){ resolve(window.fdc3); } else { - reject('Desktop Agent Preload not found '); + reject("Desktop Agent Preload not found!"); } }, timeout ); // listen for the fdc3Ready event - window.addEventListener('fdc3Ready', listener, { once: true }); + window.addEventListener("fdc3Ready", listener, { once: true }); } }); }; diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 21e29100a..32543dbe7 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -99,8 +99,8 @@ Simply use the interface you've retrieved and address the API directly: ```js async function sendData(fdc3: DesktopAgent) { await fdc3.broadcast({ - type: 'fdc3.instrument', - id: { ticker: 'AAPL' } + type: "fdc3.instrument", + id: { ticker: "AAPL" } }) } @@ -113,11 +113,11 @@ await sendData(fdc3); The npm package provides a wrapper around FDC3, allowing you to use it with ES6 import syntax: ```javascript -import { raiseIntent } from '@finos/fdc3'; +import { raiseIntent } from "@finos/fdc3"; -await raiseIntent('ViewAnalysis', { - type: 'fdc3.instrument', - id: { ticker: 'AAPL' } +await raiseIntent("ViewAnalysis", { + type: "fdc3.instrument", + id: { ticker: "AAPL" } }); ``` From c5046e28e12134f2c43ba7261f3df0303d45ea4b Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 12:12:23 +0100 Subject: [PATCH 124/152] typing in loadIframe example --- docs/api/specs/webConnectionProtocol.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 4e5cad0b8..0345ddaad 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -196,17 +196,17 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: - Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: ```ts - const loadIframe = (url, loadedHandler): WindowProxy => { + const loadIframe = (url: string, loadedHandler: () => void): WindowProxy => { const ifrm = document.createElement("iframe"); iframe.onload = loadedHandler; ifrm.src = url; ifrm.style.width = "0"; ifrm.style.height = "0"; ifrm.style.visibility = "0"; - ifrm.ariaHidden="true"; + ifrm.ariaHidden = "true"; document.body.appendChild(ifrm); return ifrm.contentWindow; - } + }; ``` - Once the frame has loaded (i.e. when the `loadedHandler` in the above example runs), repeat steps 1-3 above substituting the iframe's `contentWindow` for the candidate window objects before proceeding to step 5. A new timeout should be used to limit the amount of time that the `getAgent()` implementation waits for a response. If the event that this subsequent timeout is exceeded, reject Error with the `ErrorOnConnect` message from the [`AgentError`](../ref/Errors#agenterror) enumeration. From 01f862f6cbfbeee77b31678fbbc9c6648940a45f Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 12:14:38 +0100 Subject: [PATCH 125/152] formatting and correction to WCP1Hello example --- docs/api/specs/webConnectionProtocol.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 0345ddaad..e473fa861 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -175,18 +175,18 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp ```ts const hello = { - type: "WCP1Hello", - payload: { - identityUrl: identityUrl, - actualUrl: actualUrl, - fdc3Version: "2.2", - intentResolver: true - channelSelector: true - }, - meta: { - connectionAttemptUuid: "bc96f1db-9b2b-465f-aab3-3870dc07b072" - timestamp: "2024-09-09T11:44:39+00:00" - } + type: "WCP1Hello", + payload: { + identityUrl: identityUrl, + actualUrl: actualUrl, + fdc3Version: "2.2", + intentResolver: true, + channelSelector: true + }, + meta: { + connectionAttemptUuid: "bc96f1db-9b2b-465f-aab3-3870dc07b072", + timestamp: "2024-09-09T11:44:39+00:00" + } }; candidate.postMessage(hello, { targetOrigin: "*" }); ``` From 7c089d2bde930a521cf53187abeea8782bac658e Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 12:19:33 +0100 Subject: [PATCH 126/152] minor indentation corrections --- docs/api/specs/webConnectionProtocol.md | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index e473fa861..1c997c151 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -155,7 +155,7 @@ const discoverProxyCandidates = (): WindowProxy[] => { //parent frames let currentWin = window; - while (currentWin.parent !== currentWin){ + while (currentWin.parent !== currentWin) { candidates.push(currentWin.parent); currentWin = currentWin.parent; } @@ -357,21 +357,21 @@ However, Browser Resident Desktop Agents may have difficulty displaying user int ```mermaid flowchart LR - subgraph DA [Desktop Agent Window] - A[(Desktop Agent)] + subgraph DA [Desktop Agent Window] + A[(Desktop Agent)] + end + A-->B["getAgent()"] + subgraph App [App Window] + B + subgraph iframe1 [iframe 1] + cs[Channel Selector] end - A-->B["getAgent()"] - subgraph App [App Window] - B - subgraph iframe1 [iframe 1] - cs[Channel Selector] - end - subgraph iframe2 [iframe 2] - ir[Intent Resolver] - end + subgraph iframe2 [iframe 2] + ir[Intent Resolver] end - B-->cs - B-->ir + end + B-->cs + B-->ir ``` The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via to following messages: From 9f0bc4d0853c0707284b8ff89a1c950f5ff3637e Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 14:27:38 +0100 Subject: [PATCH 127/152] Typos in Preload Desktop Agents spec --- docs/api/specs/preloadDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/preloadDesktopAgents.md b/docs/api/specs/preloadDesktopAgents.md index 5686ea358..9e25259ce 100644 --- a/docs/api/specs/preloadDesktopAgents.md +++ b/docs/api/specs/preloadDesktopAgents.md @@ -5,7 +5,7 @@ title: Preload Desktop Agents (next) --- -A Preload Desktop Agent is an FDC3 Desktop Agents (DA) supporting web applications that 'injects' or 'preloads' scripts into web windows which provide access to an FDC3 Desktop Agent API implementation. This document specifies the required behavior for a Preload Desktop Agent (DA). +A Preload Desktop Agent is an FDC3 Desktop Agent (DA) supporting web applications that 'inject' or 'preload' scripts into web windows which provide access to an FDC3 Desktop Agent API implementation. This document specifies the required behavior for a Preload Desktop Agent (DA). :::info From 8c2cdfebfa99d0ac87ba9505f5b56af16e29b53a Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 14:44:22 +0100 Subject: [PATCH 128/152] Typos in Browser Resident Desktop Agents spec --- docs/api/specs/browserResidentDesktopAgents.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index dfe2579c8..873d20472 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -6,15 +6,15 @@ title: Browser-Resident Desktop Agents (next) :::info _[@experimental](../fdc3-compliance#experimental-features)_ -Browser Resident Desktop Agents are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in future versions and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +Browser Resident Desktop Agents (DAs) are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in future versions and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. ::: -This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by the [`@finos.fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) and a standardized communication protocol. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView2 or a browser-extension based implementation). +This document specifies the required behavior for Browser-Resident Desktop Agents (DA). Such agents allow FDC3 applications running directly in a browser to participate in FDC3 interop by way of a `getAgent()` function that is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) and a standardized communication protocol. This approach is in contrast to "Preload DAs" which run on technology that allows the FDC3 interface to be injected (such as Electron, WebView2 or a browser-extension based implementation). This specification only applies to apps running in a browser and therefore assumes use of JavaScript/TypeScript and HTML APIs. Implementations in other languages such as .NET are not covered. -Along with this specification, a new general connection strategy has been established for FDC3 compliant web-applications: FDC3 compliant apps SHOULD make use of `getAgent()` function provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) to retrieve their FDC3 interface (an instance of an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or [Preload DAs](./preloadDesktopAgents) without code modification. We refer to this concept as Write Once Run Anywhere (WORA). +Along with this specification, a new general connection strategy has been established for FDC3 compliant web-applications: FDC3 compliant apps SHOULD make use of `getAgent()` function provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3) to retrieve their FDC3 interface (an instance of an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface). Apps that follow these guidelines will be able to interop through either Browser-Resident DAs or [Preload DAs](./preloadDesktopAgents) without the inclusion of code or libraries specific to a particular Desktop Agent vendor or implementation. We refer to this concept as Write Once Run Anywhere (WORA). :::info @@ -24,21 +24,21 @@ Prior to FDC3 2.2, only [Preload Desktop Agents](./preloadDesktopAgents) were su :::note -This document only covers the requirements for _implementors of Browser-Resident DAs_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3). Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates. +This document covers the requirements for _implementors of Browser-Resident Desktop Agents_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3). Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates or [supported platforms](../supported-platforms) for details of how to access the Desktop Agent API in an application. ::: :::tip -When referencing "DA" in this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. +When referencing "DA" in the subsequent sections of this document we will hereafter always mean a "Browser-Resident Desktop Agent" - code that runs in a browser page (iframe or window) and which conforms to this specification. ::: ## Launching apps -As a prerequisite for running FDC3 in the browser, a DA must first exist as running code in a browser window (See failover functions for an exception to this rule), although that code MAY also connect to or rely on remotely hosted services. We will refer to this window as the "DA Window". +As a prerequisite for launching an app via FDC3 in the browser, a DA must first exist as running code in a browser window (See failover functions for an exception to this rule), although that code MAY also connect to or rely on remotely hosted services. We will refer to this window as the "DA Window". -As the DA typically acts as a launcher for applications, it will often be the case that the DA window is related to the application window(s) in that it may have create the application window with `window.open()` or by creating an iframe and loading the application URL into it. Hence, the DA window may be referred to as a 'parent' (window or frame) of the application frame and the relationship may be used to implement communication between the frames. +As the DA typically acts as a launcher for applications, it will often be the case that the DA window is related to the application window(s) in that it may have created the application window with `window.open()` or have created an iframe and loaded the application URL into it. Hence, the DA window may be referred to as a 'parent' (window or frame) of the application frame and the relationship may be used to implement communication between the frames. :::note @@ -46,7 +46,7 @@ It is possible to have multiple DA Windows. For instance, a DA may propagate its ::: -When an app runs `getAgent()`, it checks for the existence of `window.parent`, `window.opener` and `window.parent.opener` (and will continue up the chain of parent frames, e.g. `window.parent.parent`, `window.parent.parent.opener` until the reference to the next parent is equal to the current one (e.g. `window.parent.parent === window.parent` indicating that the frame does not have a parent). `getAgent()` will then send a standardized `WCP1Hello` message to each parent window or frame reference via [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) in to discover a DA. +When an app runs `getAgent()`, it checks for the existence of `window.parent`, `window.opener` and `window.parent.opener` (and will continue up the chain of parent frames, e.g. `window.parent.parent`, `window.parent.parent.opener` until the reference to the next parent is equal to the current one (e.g. `window.parent.parent === window.parent` indicating that the frame does not have a parent). `getAgent()` will then send a standardized `WCP1Hello` message to each parent window or frame reference via [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) in order to discover a DA. Hence, apps may be launched: From e3ce1dbc8c18624e42dcb5090059cf93daae20e2 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 15:42:21 +0100 Subject: [PATCH 129/152] missing space in browser spec --- docs/api/specs/browserResidentDesktopAgents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 873d20472..48b78700b 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -58,7 +58,7 @@ and the Desktop Agent application will be found in a 'parent' of the application ## Responding to app instance connections - Web Connection Protocol (WCP) -Browser Resident DAs MUST call `window.addEventListener("message",...)` to receive incoming connection requests from apps, in the form of [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) messages defined in the [Web Connection Protocol](./webConnectionProtocol). +Browser Resident DAs MUST call `window.addEventListener("message", ...)` to receive incoming connection requests from apps, in the form of [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) messages defined in the [Web Connection Protocol](./webConnectionProtocol). Upon receiving an incoming [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) the Desktop Agent MUST either: From dda1f278fae1ca37f6ba4c41cbbba644ff6ae3ea Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 15:42:48 +0100 Subject: [PATCH 130/152] make error enums consistent --- docs/api/ref/Errors.md | 57 +++++++++++++++++---------------- src/api/Errors.ts | 72 +++++++++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 7a4a829e6..7ea8dd3e5 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -33,13 +33,13 @@ enum AgentError { /** Returned if either the failover function itself, or what it returned, * was not the right type. */ - InvalidFailover = "InvalidFailover" + InvalidFailover = "InvalidFailover", } ``` +
- ## `ChannelError` Contains constants representing the errors that can be encountered when calling channels using the [`joinUserChannel`](DesktopAgent#joinuserchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](Channel#getcurrentcontext), [`broadcast`](Channel#broadcast) or [`addContextListener`](Channel#addcontextlistener) methods on the `Channel` object. @@ -52,24 +52,24 @@ enum ChannelError { /** Returned if the specified channel is not found when attempting to join a * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`). */ - NoChannelFound = 'NoChannelFound', + NoChannelFound = "NoChannelFound", /** SHOULD be returned when a request to join a user channel or to a retrieve * a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods * of the DesktopAgent (`fdc3`) object is denied. */ - AccessDenied = 'AccessDenied', + AccessDenied = "AccessDenied", /** SHOULD be returned when a channel cannot be created or retrieved via the * `getOrCreateChannel` method of the DesktopAgent (`fdc3`). */ - CreationFailed = 'CreationFailed', + CreationFailed = "CreationFailed", /** Returned if a call to the `broadcast` functions is made with an invalid * context argument. Contexts should be Objects with at least a `type` field * that has a `string` value. */ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", } ``` @@ -129,30 +129,30 @@ Contains constants representing the errors that can be encountered when calling ```ts enum OpenError { /** Returned if the specified application is not found.*/ - AppNotFound = 'AppNotFound', + AppNotFound = "AppNotFound", /** Returned if the specified application fails to launch correctly.*/ - ErrorOnLaunch = 'ErrorOnLaunch', + ErrorOnLaunch = "ErrorOnLaunch", /** Returned if the specified application launches but fails to add a context * listener in order to receive the context passed to the `fdc3.open` call. */ - AppTimeout = 'AppTimeout', + AppTimeout = "AppTimeout", /** Returned if the FDC3 desktop agent implementation is not currently able * to handle the request. */ - ResolverUnavailable = 'ResolverUnavailable', + ResolverUnavailable = "ResolverUnavailable", /** Returned if a call to the `open` function is made with an invalid * context argument. Contexts should be Objects with at least a `type` field * that has a `string` value. */ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", /** @experimental Returned if the specified Desktop Agent is not found, via a connected * Desktop Agent Bridge. */ - DesktopAgentNotFound = 'DesktopAgentNotFound', + DesktopAgentNotFound = "DesktopAgentNotFound", } ``` @@ -212,49 +212,49 @@ export enum ResolveError { /** SHOULD be returned if no apps are available that can resolve the intent * and context combination. */ - NoAppsFound = 'NoAppsFound', + NoAppsFound = "NoAppsFound", /** Returned if the FDC3 desktop agent implementation is not currently able * to handle the request. */ - ResolverUnavailable = 'ResolverUnavailable', + ResolverUnavailable = "ResolverUnavailable", /** Returned if the user cancelled the resolution request, for example by * closing or cancelling a resolver UI. */ - UserCancelled = 'UserCancelledResolution', + UserCancelled = "UserCancelledResolution", /** SHOULD be returned if a timeout cancels an intent resolution that * required user interaction. Please use `ResolverUnavailable` instead for * situations where a resolver UI or similar fails. */ - ResolverTimeout = 'ResolverTimeout', + ResolverTimeout = "ResolverTimeout", /** Returned if a specified target application is not available or a new * instance of it cannot be opened. */ - TargetAppUnavailable = 'TargetAppUnavailable', + TargetAppUnavailable = "TargetAppUnavailable", /** Returned if a specified target application instance is not available, * for example because it has been closed. */ - TargetInstanceUnavailable = 'TargetInstanceUnavailable', + TargetInstanceUnavailable = "TargetInstanceUnavailable", /** Returned if the intent and context could not be delivered to the selected * application or instance, for example because it has not added an intent * handler within a timeout. */ - IntentDeliveryFailed = 'IntentDeliveryFailed', + IntentDeliveryFailed = "IntentDeliveryFailed", /** Returned if a call to one of the `raiseIntent` functions is made with an * invalid context argument. Contexts should be Objects with at least a `type` * field that has a `string` value. */ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", /** @experimental Returned if the specified Desktop Agent is not found, via a connected * Desktop Agent Bridge. */ - DesktopAgentNotFound = 'DesktopAgentNotFound', + DesktopAgentNotFound = "DesktopAgentNotFound", } ``` @@ -339,12 +339,12 @@ enum ResultError { /** Returned if the intent handler exited without returning a valid result * (a promise resolving to a Context, Channel object or void). */ - NoResultReturned = 'NoResultReturned', + NoResultReturned = "NoResultReturned", /** Returned if the `IntentHandler` function processing the raised intent * throws an error or rejects the Promise it returned. */ - IntentHandlerRejected = 'IntentHandlerRejected', + IntentHandlerRejected = "IntentHandlerRejected", } ``` @@ -388,20 +388,23 @@ public static class ResultError enum BridgingError { /** @experimental Returned if a Desktop Agent did not return a response, via * Desktop Agent Bridging, within the allotted timeout. */ - ResponseTimedOut = 'ResponseToBridgeTimedOut', + ResponseTimedOut = "ResponseToBridgeTimedOut", + /** @experimental Returned if a Desktop Agent that has been targeted by a * particular request has been disconnected from the Bridge before a * response has been received from it. */ - AgentDisconnected = 'AgentDisconnected', + AgentDisconnected = "AgentDisconnected", + /** @experimental Returned for FDC3 API calls that are specified with * arguments indicating that a remote Desktop agent should be targeted * (e.g. raiseIntent with an app on a remote DesktopAgent targeted), * when the local Desktop Agent is not connected to a bridge. */ - NotConnectedToBridge = 'NotConnectedToBridge', + NotConnectedToBridge = "NotConnectedToBridge", + /** @experimental Returned if a message to a Bridge deviates from the schema * for that message sufficiently that it could not be processed. */ - MalformedMessage = 'MalformedMessage' + MalformedMessage = "MalformedMessage", } ``` diff --git a/src/api/Errors.ts b/src/api/Errors.ts index cf5d26b8c..b73182fa5 100644 --- a/src/api/Errors.ts +++ b/src/api/Errors.ts @@ -23,72 +23,92 @@ export enum AgentError { /** Returned if either the failover function itself, or what it returned, * was not the right type. */ - InvalidFailover = "InvalidFailover" + InvalidFailover = "InvalidFailover", }; /** Constants representing the errors that can be encountered when calling the `open` method on the DesktopAgent object (`fdc3`). */ export enum OpenError { /** Returned if the specified application is not found.*/ - AppNotFound = 'AppNotFound', + AppNotFound = "AppNotFound", + /** Returned if the specified application fails to launch correctly.*/ - ErrorOnLaunch = 'ErrorOnLaunch', + ErrorOnLaunch = "ErrorOnLaunch", + /** Returned if the specified application launches but fails to add a context listener in order to receive the context passed to the `fdc3.open` call.*/ - AppTimeout = 'AppTimeout', + AppTimeout = "AppTimeout", + /** Returned if the FDC3 desktop agent implementation is not currently able to handle the request.*/ - ResolverUnavailable = 'ResolverUnavailable', + ResolverUnavailable = "ResolverUnavailable", + /** Returned if a call to the `open` function is made with an invalid context argument. Contexts should be Objects with at least a `type` field that has a `string` value.*/ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", + /** @experimental Returned if the specified Desktop Agent is not found, via a connected Desktop Agent Bridge.*/ - DesktopAgentNotFound = 'DesktopAgentNotFound', + DesktopAgentNotFound = "DesktopAgentNotFound", } /** Constants representing the errors that can be encountered when calling the `findIntent`, `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the DesktopAgent (`fdc3`). */ export enum ResolveError { /** SHOULD be returned if no apps are available that can resolve the intent and context combination.*/ - NoAppsFound = 'NoAppsFound', + NoAppsFound = "NoAppsFound", + /** Returned if the FDC3 desktop agent implementation is not currently able to handle the request.*/ - ResolverUnavailable = 'ResolverUnavailable', + ResolverUnavailable = "ResolverUnavailable", + /** Returned if the user cancelled the resolution request, for example by closing or cancelling a resolver UI.*/ - UserCancelled = 'UserCancelledResolution', + UserCancelled = "UserCancelledResolution", + /** SHOULD be returned if a timeout cancels an intent resolution that required user interaction. Please use `ResolverUnavailable` instead for situations where a resolver UI or similar fails.*/ - ResolverTimeout = 'ResolverTimeout', + ResolverTimeout = "ResolverTimeout", + /** Returned if a specified target application is not available or a new instance of it cannot be opened. */ - TargetAppUnavailable = 'TargetAppUnavailable', + TargetAppUnavailable = "TargetAppUnavailable", + /** Returned if a specified target application instance is not available, for example because it has been closed. */ - TargetInstanceUnavailable = 'TargetInstanceUnavailable', + TargetInstanceUnavailable = "TargetInstanceUnavailable", + /** Returned if the intent and context could not be delivered to the selected application or instance, for example because it has not added an intent handler within a timeout.*/ - IntentDeliveryFailed = 'IntentDeliveryFailed', + IntentDeliveryFailed = "IntentDeliveryFailed", + /** Returned if a call to one of the `raiseIntent` functions is made with an invalid context argument. Contexts should be Objects with at least a `type` field that has a `string` value.*/ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", + /** @experimental Returned if the specified Desktop Agent is not found, via a connected Desktop Agent Bridge.*/ - DesktopAgentNotFound = 'DesktopAgentNotFound', + DesktopAgentNotFound = "DesktopAgentNotFound", } export enum ResultError { /** Returned if the intent handler exited without returning a valid result (a promise resolving to a Context, Channel object or void). */ - NoResultReturned = 'NoResultReturned', + NoResultReturned = "NoResultReturned", + /** Returned if the Intent handler function processing the raised intent throws an error or rejects the Promise it returned. */ - IntentHandlerRejected = 'IntentHandlerRejected', + IntentHandlerRejected = "IntentHandlerRejected", } export enum ChannelError { /** Returned if the specified channel is not found when attempting to join a channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ - NoChannelFound = 'NoChannelFound', + NoChannelFound = "NoChannelFound", + /** SHOULD be returned when a request to join a user channel or to a retrieve a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods of the DesktopAgent (`fdc3`) object is denied. */ - AccessDenied = 'AccessDenied', + AccessDenied = "AccessDenied", + /** SHOULD be returned when a channel cannot be created or retrieved via the `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ - CreationFailed = 'CreationFailed', + CreationFailed = "CreationFailed", + /** Returned if a call to the `broadcast` functions is made with an invalid context argument. Contexts should be Objects with at least a `type` field that has a `string` value.*/ - MalformedContext = 'MalformedContext', + MalformedContext = "MalformedContext", } export enum BridgingError { /** @experimental Returned if a Desktop Agent did not return a response, via Desktop Agent Bridging, within the alloted timeout. */ - ResponseTimedOut = 'ResponseToBridgeTimedOut', + ResponseTimedOut = "ResponseToBridgeTimedOut", + /** @experimental Returned if a Desktop Agent that has been targeted by a particular request has been disconnected from the Bridge before a response has been received from it. */ - AgentDisconnected = 'AgentDisconnected', + AgentDisconnected = "AgentDisconnected", + /** @experimental Returned for FDC3 API calls that are specified with arguments indicating that a remote Desktop agent should be targeted (e.g. raiseIntent with an app on a remote DesktopAgent targeted), when the local Desktop Agent is not connected to a bridge. */ - NotConnectedToBridge = 'NotConnectedToBridge', + NotConnectedToBridge = "NotConnectedToBridge", + /** @experimental Returned if a message to a Bridge deviates from the schema for that message sufficiently that it could not be processed. */ - MalformedMessage = 'MalformedMessage', + MalformedMessage = "MalformedMessage", } From 3c087a84cfb49641e45c8f56a036d2a118856849 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 15:51:46 +0100 Subject: [PATCH 131/152] switch to `const desktopAgent` from `const fdc3` to break form the past --- docs/api/ref/GetAgent.md | 8 ++++---- docs/api/supported-platforms.md | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 7b8d99d13..c99283c92 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -16,7 +16,7 @@ The `getAgent()` function is the recommended way for **web applications** to con import { getAgent, DesktopAgent } from "@finos/fdc3"; try { - const fdc3: DesktopAgent = await getAgent(); + const desktopAgent: DesktopAgent = await getAgent(); //do FDC3 things here } catch (e: AgentError) { // Failed to connect @@ -30,7 +30,7 @@ try { import { getAgent } from "@finos/fdc3"; try { - const fdc3 = await getAgent(); + const desktopAgent = await getAgent(); //do FDC3 things here } catch (e) { // Failed to connect @@ -122,8 +122,8 @@ Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is Example: Decreasing the timeout and providing a failover function ```js - const fdc3 = await getAgent({ - appId: “myApp@yourorg.org”, + const desktopAgent = await getAgent({ + appId: "myApp@yourorg.org", timeout: 250, failover: async (params: GetAgentParams) => { // return WindowProxy | DesktopAgent diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 32543dbe7..5e8d872b5 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -19,7 +19,7 @@ import { DesktopAgent, getAgent } from "@finos/fdc3"; //... try { - const fdc3: DesktopAgent = await getAgent(); + const desktopAgent: DesktopAgent = await getAgent(); //do FDC3 things here } catch (e: AgentError) { //connection failed @@ -27,7 +27,7 @@ try { //OR -getAgent().then((fdc3: DesktopAgent) => { +getAgent().then((desktopAgent: DesktopAgent) => { //do FDC3 things here }).catch((e: AgentError) => { //connection failed @@ -97,15 +97,15 @@ Once you've retrieved a `DesktopAgent` interface, there are two main ways FDC3 c Simply use the interface you've retrieved and address the API directly: ```js -async function sendData(fdc3: DesktopAgent) { - await fdc3.broadcast({ +async function sendData(desktopAgent: DesktopAgent) { + await desktopAgent.broadcast({ type: "fdc3.instrument", id: { ticker: "AAPL" } }) } -const fdc3: DesktopAgent = await getAgent(); -await sendData(fdc3); +const desktopAgent: DesktopAgent = await getAgent(); +await sendData(desktopAgent); ``` #### 2. es6-style Function Wrappers From 03d1c919cc18655438e6e8e9f41c17fc5447225d Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 17:10:17 +0100 Subject: [PATCH 132/152] Changing the name of iframe messages (namespacing) to avoid potential conflicts --- .../api/specs/browserResidentDesktopAgents.md | 6 +- .../desktopAgentCommunicationProtocol.md | 18 +- docs/api/specs/webConnectionProtocol.md | 2 + ...3UserInterfaceChannelSelected.schema.json} | 18 +- ... => fdc3UserInterfaceChannels.schema.json} | 16 +- ...json => fdc3UserInterfaceDrag.schema.json} | 16 +- ...=> fdc3UserInterfaceHandshake.schema.json} | 18 +- ...son => fdc3UserInterfaceHello.schema.json} | 22 +- .../api/fdc3UserInterfaceMessage.schema.json | 34 + ...n => fdc3UserInterfaceResolve.schema.json} | 18 +- ...dc3UserInterfaceResolveAction.schema.json} | 28 +- ...n => fdc3UserInterfaceRestyle.schema.json} | 20 +- schemas/api/iFrameMessage.schema.json | 34 - src/api/BrowserTypes.ts | 1899 ++++++++--------- 14 files changed, 1075 insertions(+), 1074 deletions(-) rename schemas/api/{iFrameChannelSelected.schema.json => fdc3UserInterfaceChannelSelected.schema.json} (57%) rename schemas/api/{iFrameChannels.schema.json => fdc3UserInterfaceChannels.schema.json} (74%) rename schemas/api/{iFrameDrag.schema.json => fdc3UserInterfaceDrag.schema.json} (75%) rename schemas/api/{iFrameHandshake.schema.json => fdc3UserInterfaceHandshake.schema.json} (50%) rename schemas/api/{iFrameHello.schema.json => fdc3UserInterfaceHello.schema.json} (69%) create mode 100644 schemas/api/fdc3UserInterfaceMessage.schema.json rename schemas/api/{iFrameResolve.schema.json => fdc3UserInterfaceResolve.schema.json} (67%) rename schemas/api/{iFrameResolveAction.schema.json => fdc3UserInterfaceResolveAction.schema.json} (61%) rename schemas/api/{iFrameRestyle.schema.json => fdc3UserInterfaceRestyle.schema.json} (69%) delete mode 100644 schemas/api/iFrameMessage.schema.json diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 48b78700b..2fdfb8510 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -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. diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 39ea23c1e..7588f6e9f 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -439,19 +439,19 @@ Additional procedures are defined in the [Browser Resident Desktop Agents specif Desktop Agent implementations, such as those based on the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents) and [Web Connection Protocol](./webConnectionProtocol), may either provide their own user interfaces (or other appropriate mechanisms) for the selection of User Channels or Intent Resolution, or they may work with implementations injected into the application (for example, as described in the [Web Connection Protocol](./webConnectionProtocol#providing-channel-selector-and-intent-resolver-uis) and implemented in [`getAgent()`](../ref/GetAgent)). -Where injected user interfaces are used, standardized messaging is needed to communicate with those interfaces. This is provided in the DACP via the following 'iframe' messages, which are governed by the [`iFrameMessage`](https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json) schema. The following messages are provided: +Where injected user interfaces are used, standardized messaging is needed to communicate with those interfaces. This is provided in the DACP via the following 'iframe' messages, which are governed by the [`Fdc3UserInterfaceMessage`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterface.schema.json) schema. The following messages are provided: -- [`iFrameHello`](https://fdc3.finos.org/schemas/next/api/iFrameHello.schema.json): Sent by the iframe to its `window.parent` frame to initiate communication and to provide initial CSS to apply to the frame. This message should have a `MessagePort` appended over which further communication will be conducted. -- [`iFrameHandshake`](https://fdc3.finos.org/schemas/next/api/iFrameHandshake.schema.json): Response to the `iFrameHello` message sent by the application frame, which should be sent over the `MessagePort`. Includes details of the FDC3 version that the application is using. -- [`iFrameDrag`](https://fdc3.finos.org/schemas/next/api/iFrameDrag.schema.json): Message sent by the iframe to indicate that it is being dragged to a new position and including offsets to indicate direction and distance. -- [`iFrameRestyle`](https://fdc3.finos.org/schemas/next/api/iFrameRestyle.schema.json): Message sent by the iframe to indicate that its frame should have updated CSS applied to it, for example to support a channel selector interface that can be 'popped open' or an intent resolver that wishes to resize itself to show additional content. +- [`Fdc3UserInterfaceHello`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceHello.schema.json): Sent by the iframe to its `window.parent` frame to initiate communication and to provide initial CSS to apply to the frame. This message should have a `MessagePort` appended over which further communication will be conducted. +- [`Fdc3UserInterfaceHandshake`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceHandshake.schema.json): Response to the `Fdc3UserInterfaceHello` message sent by the application frame, which should be sent over the `MessagePort`. Includes details of the FDC3 version that the application is using. +- [`Fdc3UserInterfaceDrag`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceDrag.schema.json): Message sent by the iframe to indicate that it is being dragged to a new position and including offsets to indicate direction and distance. +- [`Fdc3UserInterfaceRestyle`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceRestyle.schema.json): Message sent by the iframe to indicate that its frame should have updated CSS applied to it, for example to support a channel selector interface that can be 'popped open' or an intent resolver that wishes to resize itself to show additional content. Messages are also provided that are specific to each interface type provided by a Desktop Agent. The following messages are specific to Channel Selector user interfaces: -- [`iFrameChannels`](https://fdc3.finos.org/schemas/next/api/iFrameChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `iFrameHandshake` and before making the injected iframe visible. -- [`iFrameChannelSelected`](https://fdc3.finos.org/schemas/next/api/iFrameChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. +- [`fdc3UserInterfaceChannels`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `fdc3UserInterfaceHandshake` and before making the injected iframe visible. +- [`fdc3UserInterfaceChannelSelected`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. Messages specific to Intent Resolver user interfaces: -- [`iFrameResolve`](https://fdc3.finos.org/schemas/next/api/iFrameResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. -- [`iFrameResolveAction`](https://fdc3.finos.org/schemas/next/api/iFrameResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. +- [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. +- [`fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 1c997c151..31668dab9 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -286,6 +286,8 @@ SessionStorage is ephemeral, only existing for the duration of the window's life Resolve the `getAgent()` promise with an object containing either a `DesktopAgent` implementation (that was found at `window.fdc3` or returned by a `failover` function) or a 'Desktop Agent Proxy' implementation (a class implementing the `DesktopAgent` interface that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent over the `MessagePort`). +Where a `DesktopAgent` or 'Desktop Agent Proxy' implementation was successfully returned, any subsequent calls to `getAgent()` that are not preceded by a navigation or refresh event, should resolve to the same instance. + ### Step 5: Disconnection Desktop Agent Preload interfaces, as used in container-based Desktop Agent implementations, are usually able to track the lifecycle and current URL of windows that host web apps in their scope. Hence, this is currently no requirement nor means for an app to indicate that it is closing, rather it is the responsibility of the Desktop Agent to update its internal state when an app closes or changes identity. diff --git a/schemas/api/iFrameChannelSelected.schema.json b/schemas/api/fdc3UserInterfaceChannelSelected.schema.json similarity index 57% rename from schemas/api/iFrameChannelSelected.schema.json rename to schemas/api/fdc3UserInterfaceChannelSelected.schema.json index f88484aa2..c49ec9e56 100644 --- a/schemas/api/iFrameChannelSelected.schema.json +++ b/schemas/api/fdc3UserInterfaceChannelSelected.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannelSelected.schema.json", - "title": "iframe Channel Selected", - "description": "Message from the channel selector UI to the DA proxy sent when the channel selection changes.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannelSelected.schema.json", + "title": "Fdc3 UserInterface Channel Selected", + "description": "Message from a channel selector UI to the DA proxy sent when the channel selection changes.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeChannelSelectedBase" + "$ref": "#/$defs/Fdc3UserInterfaceChannelSelectedBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeChannelSelectedBase": { + "Fdc3UserInterfaceChannelSelectedBase": { "type": "object", "properties": { "type": { - "title": "iframeChannelSelected Message Type", - "const": "iframeChannelSelected" + "title": "Fdc3 UserInterface ChannelSelected Message Type", + "const": "Fdc3UserInterfaceChannelSelected" }, "payload": { - "title": "iframeChannelSelected Payload", + "title": "Fdc3 UserInterface ChannelSelected Payload", "type": "object", "properties": { "selected": { diff --git a/schemas/api/iFrameChannels.schema.json b/schemas/api/fdc3UserInterfaceChannels.schema.json similarity index 74% rename from schemas/api/iFrameChannels.schema.json rename to schemas/api/fdc3UserInterfaceChannels.schema.json index c92ff72b8..3999823d8 100644 --- a/schemas/api/iFrameChannels.schema.json +++ b/schemas/api/fdc3UserInterfaceChannels.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeChannels.schema.json", - "title": "iframe Channels", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannels.schema.json", + "title": "Fdc3 UserInterface Channels", "description": "Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an iframe with the channel definitions and current channel selection.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeChannelsBase" + "$ref": "#/$defs/Fdc3UserInterfaceChannelsBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeChannelsBase": { + "Fdc3UserInterfaceChannelsBase": { "type": "object", "properties": { "type": { - "title": "iframeChannels Message Type", - "const": "iframeChannels" + "title": "Fdc3 UserInterface Channels Message Type", + "const": "Fdc3UserInterfaceChannels" }, "payload": { - "title": "iframeChannels Payload", + "title": "Fdc3 UserInterface Channels Payload", "type": "object", "properties": { "userChannels": { diff --git a/schemas/api/iFrameDrag.schema.json b/schemas/api/fdc3UserInterfaceDrag.schema.json similarity index 75% rename from schemas/api/iFrameDrag.schema.json rename to schemas/api/fdc3UserInterfaceDrag.schema.json index 4f16e3573..7734f9f58 100644 --- a/schemas/api/iFrameDrag.schema.json +++ b/schemas/api/fdc3UserInterfaceDrag.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeDrag.schema.json", - "title": "iframe Drag", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceDrag.schema.json", + "title": "Fdc3 UserInterface Drag", "description": "Message from a UI iframe to the DA proxy (setup by `getAgent()`) indicating that the user is dragging the UI to a new location and providing the offset to apply to the location. The DA proxy implementation should limit the location to the current bounds of the window's viewport", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeDragBase" + "$ref": "#/$defs/Fdc3UserInterfaceDragBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeDragBase": { + "Fdc3UserInterfaceDragBase": { "type": "object", "properties": { "type": { - "title": "iframeDrag Message Type", - "const": "iframeDrag" + "title": "Fdc3 UserInterface Drag Message Type", + "const": "Fdc3UserInterfaceDrag" }, "payload": { - "title": "iframeDrag Payload", + "title": "Fdc3 UserInterface Drag Payload", "type": "object", "properties": { "mouseOffsets": { diff --git a/schemas/api/iFrameHandshake.schema.json b/schemas/api/fdc3UserInterfaceHandshake.schema.json similarity index 50% rename from schemas/api/iFrameHandshake.schema.json rename to schemas/api/fdc3UserInterfaceHandshake.schema.json index 7d96825f3..9b4fb181f 100644 --- a/schemas/api/iFrameHandshake.schema.json +++ b/schemas/api/fdc3UserInterfaceHandshake.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeHandshake.schema.json", - "title": "iframe Handshake", - "description": "Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) over the `MessagePort` provide in the preceding iFrameHello message, confirming that it is listening to the `MessagePort` for further communication.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceHandshake.schema.json", + "title": "Fdc3 UserInterface Handshake", + "description": "Handshake message sent back to a user interface from the DA proxy code (setup by `getAgent()`) over the `MessagePort` provide in the preceding iFrameHello message, confirming that it is listening to the `MessagePort` for further communication.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeHandshakeBase" + "$ref": "#/$defs/Fdc3UserInterfaceHandshakeBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeHandshakeBase": { + "Fdc3UserInterfaceHandshakeBase": { "type": "object", "properties": { "type": { - "title": "iframeHandshake Message Type", - "const": "iframeHandshake" + "title": "Fdc3 UserInterface Handshake Message Type", + "const": "Fdc3UserInterfaceHandshake" }, "payload": { - "title": "iframeHandshake Payload", + "title": "Fdc3 UserInterface Handshake Payload", "type": "object", "properties": { "fdc3Version": { diff --git a/schemas/api/iFrameHello.schema.json b/schemas/api/fdc3UserInterfaceHello.schema.json similarity index 69% rename from schemas/api/iFrameHello.schema.json rename to schemas/api/fdc3UserInterfaceHello.schema.json index 2f88862d8..c8ad9a152 100644 --- a/schemas/api/iFrameHello.schema.json +++ b/schemas/api/fdc3UserInterfaceHello.schema.json @@ -1,38 +1,38 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeHello.schema.json", - "title": "iframe Hello", - "description": "Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate, containing initial CSS to set on the iframe, and including an appended `MessagePort` to be used for further communication.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceHello.schema.json", + "title": "Fdc3 UserInterface Hello", + "description": "Hello message sent by a UI to the Desktop Agent proxy setup by `getAgent()` to indicate it is ready to communicate, containing initial CSS to set on the iframe, and including an appended `MessagePort` to be used for further communication.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeHelloBase" + "$ref": "#/$defs/Fdc3UserInterfaceHelloBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeHelloBase": { + "Fdc3UserInterfaceHelloBase": { "type": "object", "properties": { "type": { - "title": "iframeHello Message Type", - "const": "iframeHello" + "title": "Fdc3 UserInterface Hello Message Type", + "const": "Fdc3UserInterfaceHello" }, "payload": { - "title": "iframeHello Payload", + "title": "Fdc3 UserInterface Hello Payload", "type": "object", "properties": { "implementationDetails": { "title": "Implementation Details", "type": "string", - "description": "Details about the UI implementation in the iframe, such as vendor and version, for logging purposes." + "description": "Details about the UI implementation, such as vendor and version, for logging purposes." }, "initialCSS": { "title": "Initial CSS", "type": "object", - "description": "A constrained set of CSS properties that should be set on the iframe before it is displayed. Note `position` cannot be specified and should always be set to `fixed`.", + "description": "A constrained set of styling properties that should be set on the user interface before it is displayed. Note `position` cannot be specified and should always be set to `fixed`.", "properties": { "height": {"type": "string", "title": "height", "description": "The initial height of the iframe"}, "width": {"type": "string", "title": "width", "description": "The initial width of the iframe"}, diff --git a/schemas/api/fdc3UserInterfaceMessage.schema.json b/schemas/api/fdc3UserInterfaceMessage.schema.json new file mode 100644 index 000000000..42c6e80c7 --- /dev/null +++ b/schemas/api/fdc3UserInterfaceMessage.schema.json @@ -0,0 +1,34 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceMessage.schema.json", + "title": "Fdc3 UserInterface Message", + "type": "object", + "description": "A message used to communicate with user interface frames injected by `getAgent()` for displaying UI elements such as the intent resolver or channel selector. Used for messages sent in either direction.", + "properties": { + "type": { + "title": "Fdc3 UserInterface Message type", + "type": "string", + "enum": [ + "Fdc3UserInterfaceHello", + "Fdc3UserInterfaceHandshake", + "Fdc3UserInterfaceRestyle", + "Fdc3UserInterfaceDrag", + "Fdc3UserInterfaceResolve", + "Fdc3UserInterfaceResolveAction", + "Fdc3UserInterfaceChannels", + "Fdc3UserInterfaceChannelSelected" + ], + "description": "Identifies the type of the message to or from the user interface frame." + }, + "payload": { + "title": "Message payload", + "type": "object", + "description": "The message payload", + "additionalProperties": true + } + }, + "required": [ + "type" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/schemas/api/iFrameResolve.schema.json b/schemas/api/fdc3UserInterfaceResolve.schema.json similarity index 67% rename from schemas/api/iFrameResolve.schema.json rename to schemas/api/fdc3UserInterfaceResolve.schema.json index 6e62d6b5e..1a30076b0 100644 --- a/schemas/api/iFrameResolve.schema.json +++ b/schemas/api/fdc3UserInterfaceResolve.schema.json @@ -1,27 +1,27 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeResolve.schema.json", - "title": "iframe Resolve", - "description": "Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an iframe with the resolver data to setup the UI.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json", + "title": "Fdc3 UserInterface Resolve", + "description": "Setup message sent by the DA proxy code in getAgent() to an intent resolver UI with the resolver data to setup the UI.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeResolveBase" + "$ref": "#/$defs/Fdc3UserInterfaceResolveBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeResolveBase": { + "Fdc3UserInterfaceResolveBase": { "type": "object", "properties": { "type": { - "title": "iframeResolve Message Type", - "const": "iframeResolve" + "title": "Fdc3 UserInterface Resolve Message Type", + "const": "Fdc3UserInterfaceResolve" }, "payload": { - "title": "iframeResolve Payload", + "title": "Fdc3 UserInterface Resolve Payload", "type": "object", "properties": { "context": { diff --git a/schemas/api/iFrameResolveAction.schema.json b/schemas/api/fdc3UserInterfaceResolveAction.schema.json similarity index 61% rename from schemas/api/iFrameResolveAction.schema.json rename to schemas/api/fdc3UserInterfaceResolveAction.schema.json index 1728c8c2a..67daf031f 100644 --- a/schemas/api/iFrameResolveAction.schema.json +++ b/schemas/api/fdc3UserInterfaceResolveAction.schema.json @@ -1,29 +1,29 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeResolveAction.schema.json", - "title": "iframe Resolve Action", - "description": "Message from an intent resolver UI in an iframe to DA proxy code in getAgent() reporting a user action.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json", + "title": "Fdc3 UserInterface Resolve Action", + "description": "Message from an intent resolver UI to DA proxy code in getAgent() reporting a user action.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeResolveActionBase" + "$ref": "#/$defs/Fdc3UserInterfaceResolveActionBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeResolveActionBase": { + "Fdc3UserInterfaceResolveActionBase": { "type": "object", "properties": { "type": { - "title": "iframeResolveAction Message Type", - "const": "iframeResolveAction" + "title": "Fdc3 UserInterface ResolveAction Message Type", + "const": "Fdc3UserInterfaceResolveAction" }, "payload": { "oneOf": [ - { "$ref": "#/$defs/iframeResolveActionPayload"}, - { "$ref": "#/$defs/iframeResolveCancelPayload"} + { "$ref": "#/$defs/Fdc3UserInterfaceResolveActionPayload"}, + { "$ref": "#/$defs/Fdc3UserInterfaceResolveCancelPayload"} ] } }, @@ -33,8 +33,8 @@ ], "additionalProperties": false }, - "iframeResolveActionPayload": { - "title": "iframeResolve Action Payload", + "Fdc3UserInterfaceResolveActionPayload": { + "title": "Fdc3 UserInterface Resolve Action Payload", "type": "object", "properties": { "intent": { @@ -67,8 +67,8 @@ ], "additionalProperties": false }, - "iframeResolveCancelPayload": { - "title": "iframeResolve Cancel Payload", + "Fdc3UserInterfaceResolveCancelPayload": { + "title": "Fdc3 UserInterface Resolve Cancel Payload", "type": "object", "properties": { "action": { diff --git a/schemas/api/iFrameRestyle.schema.json b/schemas/api/fdc3UserInterfaceRestyle.schema.json similarity index 69% rename from schemas/api/iFrameRestyle.schema.json rename to schemas/api/fdc3UserInterfaceRestyle.schema.json index 773d6b981..3f015038f 100644 --- a/schemas/api/iFrameRestyle.schema.json +++ b/schemas/api/fdc3UserInterfaceRestyle.schema.json @@ -1,33 +1,33 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iframeRestyle.schema.json", - "title": "iframe Restyle", - "description": "Message from a UI iframe to the DA proxy code (setup by `getAgent()`) with updated styling information to apply to the iframe. Can be used to implement a pop-open or close interaction or other transition needed by a UI implementation.", + "$id": "https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceRestyle.schema.json", + "title": "Fdc3 UserInterface Restyle", + "description": "Message from a UI frame to the DA proxy code (setup by `getAgent()`) with updated styling information to apply to it. Can be used to implement a pop-open or close interaction or other transition needed by a UI implementation.", "type": "object", "allOf": [ { - "$ref": "#/$defs/IframeRestyleBase" + "$ref": "#/$defs/Fdc3UserInterfaceRestyleBase" }, { - "$ref": "iFrameMessage.schema.json" + "$ref": "fdc3UserInterfaceMessage.schema.json" } ], "$defs": { - "IframeRestyleBase": { + "Fdc3UserInterfaceRestyleBase": { "type": "object", "properties": { "type": { - "title": "iframeRestyle Message Type", - "const": "iframeRestyle" + "title": "Fdc3 UserInterface Restyle Message Type", + "const": "Fdc3UserInterfaceRestyle" }, "payload": { - "title": "iframeRestyle Payload", + "title": "Fdc3 UserInterface Restyle Payload", "type": "object", "properties": { "updatedCSS": { "title": "Updated CSS", "type": "object", - "description": "A constrained set of CSS properties that should be applied to the iframe. Note `position` cannot be set, and should always be `fixed`.", + "description": "A constrained set of styling properties that should be applied to the frame. Note `position` cannot be set, and should always be `fixed`.", "properties": { "height": {"type": "string", "title": "height", "description": "The updated height of the iframe"}, "width": {"type": "string", "title": "width", "description": "The updated width of the iframe"}, diff --git a/schemas/api/iFrameMessage.schema.json b/schemas/api/iFrameMessage.schema.json deleted file mode 100644 index 9e5bc5256..000000000 --- a/schemas/api/iFrameMessage.schema.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/iFrameMessage.schema.json", - "title": "iframe Message", - "type": "object", - "description": "A message used to communicate with iframes injected by `getAgent()` for displaying UI elements such as the intent resolver or channel selector. Used for messages sent in either direction.", - "properties": { - "type": { - "title": "iframe Message type", - "type": "string", - "enum": [ - "iframeHello", - "iframeHandshake", - "iframeRestyle", - "iframeDrag", - "iframeResolve", - "iframeResolveAction", - "iframeChannels", - "iframeChannelSelected" - ], - "description": "Identifies the type of the message to or from the iframe." - }, - "payload": { - "title": "Message payload", - "type": "object", - "description": "The message payload", - "additionalProperties": true - } - }, - "required": [ - "type" - ], - "additionalProperties": false -} \ No newline at end of file diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index 088ee3c27..dc99c2946 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -1,6 +1,6 @@ // To parse this data: // -// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatAcknowledgementRequest, HeartbeatEvent, IframeChannels, IframeChannelSelected, IframeDrag, IframeHandshake, IframeHello, IframeMessage, IframeResolve, IframeResolveAction, IframeRestyle, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; +// import { Convert, AddContextListenerRequest, AddContextListenerResponse, AddEventListenerRequest, AddEventListenerResponse, AddIntentListenerRequest, AddIntentListenerResponse, AgentEventMessage, AgentResponseMessage, AppRequestMessage, BroadcastEvent, BroadcastRequest, BroadcastResponse, ChannelChangedEvent, ContextListenerUnsubscribeRequest, ContextListenerUnsubscribeResponse, CreatePrivateChannelRequest, CreatePrivateChannelResponse, EventListenerUnsubscribeRequest, EventListenerUnsubscribeResponse, Fdc3UserInterfaceChannels, Fdc3UserInterfaceChannelSelected, Fdc3UserInterfaceDrag, Fdc3UserInterfaceHandshake, Fdc3UserInterfaceHello, Fdc3UserInterfaceMessage, Fdc3UserInterfaceResolve, Fdc3UserInterfaceResolveAction, Fdc3UserInterfaceRestyle, FindInstancesRequest, FindInstancesResponse, FindIntentRequest, FindIntentResponse, FindIntentsByContextRequest, FindIntentsByContextResponse, GetAppMetadataRequest, GetAppMetadataResponse, GetCurrentChannelRequest, GetCurrentChannelResponse, GetCurrentContextRequest, GetCurrentContextResponse, GetInfoRequest, GetInfoResponse, GetOrCreateChannelRequest, GetOrCreateChannelResponse, GetUserChannelsRequest, GetUserChannelsResponse, HeartbeatAcknowledgementRequest, HeartbeatEvent, IntentEvent, IntentListenerUnsubscribeRequest, IntentListenerUnsubscribeResponse, IntentResultRequest, IntentResultResponse, JoinUserChannelRequest, JoinUserChannelResponse, LeaveCurrentChannelRequest, LeaveCurrentChannelResponse, OpenRequest, OpenResponse, PrivateChannelAddEventListenerRequest, PrivateChannelAddEventListenerResponse, PrivateChannelDisconnectRequest, PrivateChannelDisconnectResponse, PrivateChannelOnAddContextListenerEvent, PrivateChannelOnDisconnectEvent, PrivateChannelOnUnsubscribeEvent, PrivateChannelUnsubscribeEventListenerRequest, PrivateChannelUnsubscribeEventListenerResponse, RaiseIntentForContextRequest, RaiseIntentForContextResponse, RaiseIntentRequest, RaiseIntentResponse, RaiseIntentResultResponse, WebConnectionProtocol1Hello, WebConnectionProtocol2LoadURL, WebConnectionProtocol3Handshake, WebConnectionProtocol4ValidateAppIdentity, WebConnectionProtocol5ValidateAppIdentityFailedResponse, WebConnectionProtocol5ValidateAppIdentitySuccessResponse, WebConnectionProtocol6Goodbye, WebConnectionProtocolMessage } from "./file"; // // const addContextListenerRequest = Convert.toAddContextListenerRequest(json); // const addContextListenerResponse = Convert.toAddContextListenerResponse(json); @@ -21,6 +21,15 @@ // const createPrivateChannelResponse = Convert.toCreatePrivateChannelResponse(json); // const eventListenerUnsubscribeRequest = Convert.toEventListenerUnsubscribeRequest(json); // const eventListenerUnsubscribeResponse = Convert.toEventListenerUnsubscribeResponse(json); +// const fdc3UserInterfaceChannels = Convert.toFdc3UserInterfaceChannels(json); +// const fdc3UserInterfaceChannelSelected = Convert.toFdc3UserInterfaceChannelSelected(json); +// const fdc3UserInterfaceDrag = Convert.toFdc3UserInterfaceDrag(json); +// const fdc3UserInterfaceHandshake = Convert.toFdc3UserInterfaceHandshake(json); +// const fdc3UserInterfaceHello = Convert.toFdc3UserInterfaceHello(json); +// const fdc3UserInterfaceMessage = Convert.toFdc3UserInterfaceMessage(json); +// const fdc3UserInterfaceResolve = Convert.toFdc3UserInterfaceResolve(json); +// const fdc3UserInterfaceResolveAction = Convert.toFdc3UserInterfaceResolveAction(json); +// const fdc3UserInterfaceRestyle = Convert.toFdc3UserInterfaceRestyle(json); // const findInstancesRequest = Convert.toFindInstancesRequest(json); // const findInstancesResponse = Convert.toFindInstancesResponse(json); // const findIntentRequest = Convert.toFindIntentRequest(json); @@ -41,15 +50,6 @@ // const getUserChannelsResponse = Convert.toGetUserChannelsResponse(json); // const heartbeatAcknowledgementRequest = Convert.toHeartbeatAcknowledgementRequest(json); // const heartbeatEvent = Convert.toHeartbeatEvent(json); -// const iframeChannels = Convert.toIframeChannels(json); -// const iframeChannelSelected = Convert.toIframeChannelSelected(json); -// const iframeDrag = Convert.toIframeDrag(json); -// const iframeHandshake = Convert.toIframeHandshake(json); -// const iframeHello = Convert.toIframeHello(json); -// const iframeMessage = Convert.toIframeMessage(json); -// const iframeResolve = Convert.toIframeResolve(json); -// const iframeResolveAction = Convert.toIframeResolveAction(json); -// const iframeRestyle = Convert.toIframeRestyle(json); // const intentEvent = Convert.toIntentEvent(json); // const intentListenerUnsubscribeRequest = Convert.toIntentListenerUnsubscribeRequest(json); // const intentListenerUnsubscribeResponse = Convert.toIntentListenerUnsubscribeResponse(json); @@ -1060,74 +1060,307 @@ export interface EventListenerUnsubscribeResponse { */ /** - * A request for details of instances of a particular app. + * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an + * iframe with the channel definitions and current channel selection. * - * A request message from an FDC3-enabled app to a Desktop Agent. + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. */ -export interface FindInstancesRequest { +export interface Fdc3UserInterfaceChannels { /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + * The message payload */ - meta: AddContextListenerRequestMeta; + payload: Fdc3UserInterfaceChannelsPayload; /** - * The message payload typically contains the arguments to FDC3 API functions. + * Identifies the type of the message to or from the user interface frame. */ - payload: FindInstancesRequestPayload; + type: "Fdc3UserInterfaceChannels"; +} + +/** + * The message payload + */ +export interface Fdc3UserInterfaceChannelsPayload { /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * The id of the channel that should be currently selected, or `null` if none should be + * selected */ - type: "findInstancesRequest"; + selected: null | string; + /** + * User Channel definitions + */ + userChannels: Channel[]; } /** - * The message payload typically contains the arguments to FDC3 API functions. + * Identifies the type of the message to or from the user interface frame. */ -export interface FindInstancesRequestPayload { - app: AppIdentifier; + +/** + * Message from a channel selector UI to the DA proxy sent when the channel selection + * changes. + * + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. + */ +export interface Fdc3UserInterfaceChannelSelected { + /** + * The message payload + */ + payload: Fdc3UserInterfaceChannelSelectedPayload; + /** + * Identifies the type of the message to or from the user interface frame. + */ + type: "Fdc3UserInterfaceChannelSelected"; } /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * The message payload */ +export interface Fdc3UserInterfaceChannelSelectedPayload { + /** + * The id of the channel that should be currently selected, or `null` if none should be + * selected + */ + selected: null | string; +} /** - * A response to a findInstances request. + * Identifies the type of the message to or from the user interface frame. + */ + +/** + * Message from a UI iframe to the DA proxy (setup by `getAgent()`) indicating that the user + * is dragging the UI to a new location and providing the offset to apply to the location. + * The DA proxy implementation should limit the location to the current bounds of the + * window's viewport * - * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the - * payload contains an `error` property, the request was unsuccessful. + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. */ -export interface FindInstancesResponse { +export interface Fdc3UserInterfaceDrag { /** - * Metadata for messages sent by a Desktop Agent to an App in response to an API call + * The message payload */ - meta: AddContextListenerResponseMeta; + payload: Fdc3UserInterfaceDragPayload; /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. + * Identifies the type of the message to or from the user interface frame. */ - payload: FindInstancesResponsePayload; + type: "Fdc3UserInterfaceDrag"; +} + +/** + * The message payload + */ +export interface Fdc3UserInterfaceDragPayload { /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * The offset to move the frame by */ - type: "findInstancesResponse"; + mouseOffsets: MouseOffsets; } /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. + * The offset to move the frame by + */ +export interface MouseOffsets { + x: number; + y: number; +} + +/** + * Identifies the type of the message to or from the user interface frame. + */ + +/** + * Handshake message sent back to a user interface from the DA proxy code (setup by + * `getAgent()`) over the `MessagePort` provide in the preceding iFrameHello message, + * confirming that it is listening to the `MessagePort` for further communication. * - * The message payload contains a flag indicating whether the API call was successful, plus - * any return values for the FDC3 API function called, or indicating that the request - * resulted in an error and including a standardized error message. + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. */ -export interface FindInstancesResponsePayload { - error?: FindInstancesErrors; - appIdentifiers?: AppMetadata[]; +export interface Fdc3UserInterfaceHandshake { + /** + * The message payload + */ + payload: Fdc3UserInterfaceHandshakePayload; + /** + * Identifies the type of the message to or from the user interface frame. + */ + type: "Fdc3UserInterfaceHandshake"; +} + +/** + * The message payload + */ +export interface Fdc3UserInterfaceHandshakePayload { + /** + * The version of FDC3 API that the Desktop Agent will provide support for. + */ + fdc3Version: string; +} + +/** + * Identifies the type of the message to or from the user interface frame. + */ + +/** + * Hello message sent by a UI to the Desktop Agent proxy setup by `getAgent()` to indicate + * it is ready to communicate, containing initial CSS to set on the iframe, and including an + * appended `MessagePort` to be used for further communication. + * + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. + */ +export interface Fdc3UserInterfaceHello { + /** + * The message payload + */ + payload: Fdc3UserInterfaceHelloPayload; + /** + * Identifies the type of the message to or from the user interface frame. + */ + type: "Fdc3UserInterfaceHello"; +} + +/** + * The message payload + */ +export interface Fdc3UserInterfaceHelloPayload { + /** + * Details about the UI implementation, such as vendor and version, for logging purposes. + */ + implementationDetails: string; + /** + * A constrained set of styling properties that should be set on the user interface before + * it is displayed. Note `position` cannot be specified and should always be set to `fixed`. + */ + initialCSS: InitialCSS; +} + +/** + * A constrained set of styling properties that should be set on the user interface before + * it is displayed. Note `position` cannot be specified and should always be set to `fixed`. + */ +export interface InitialCSS { + /** + * The initial bottom property to apply to the iframe + */ + bottom?: string; + /** + * The initial height of the iframe + */ + height?: string; + /** + * The initial left property to apply to the iframe + */ + left?: string; + /** + * The maximum height to apply to the iframe + */ + maxHeight?: string; + /** + * The maximum with to apply to the iframe + */ + maxWidth?: string; + /** + * The initial right property to apply to the iframe + */ + right?: string; + /** + * The initial top property to apply to the iframe + */ + top?: string; + /** + * The transition property to apply to the iframe + */ + transition?: string; + /** + * The initial width of the iframe + */ + width?: string; + /** + * The initial zindex to apply to the iframe + */ + zIndex?: string; + [property: string]: any; +} + +/** + * Identifies the type of the message to or from the user interface frame. + */ + +/** + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. + */ +export interface Fdc3UserInterfaceMessage { + /** + * The message payload + */ + payload?: { [key: string]: any }; + /** + * Identifies the type of the message to or from the user interface frame. + */ + type: Fdc3UserInterfaceMessageType; +} + +/** + * Identifies the type of the message to or from the user interface frame. + */ +export type Fdc3UserInterfaceMessageType = "Fdc3UserInterfaceHello" | "Fdc3UserInterfaceHandshake" | "Fdc3UserInterfaceRestyle" | "Fdc3UserInterfaceDrag" | "Fdc3UserInterfaceResolve" | "Fdc3UserInterfaceResolveAction" | "Fdc3UserInterfaceChannels" | "Fdc3UserInterfaceChannelSelected"; + +/** + * Setup message sent by the DA proxy code in getAgent() to an intent resolver UI with the + * resolver data to setup the UI. + * + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. + */ +export interface Fdc3UserInterfaceResolve { + /** + * The message payload + */ + payload: Fdc3UserInterfaceResolvePayload; + /** + * Identifies the type of the message to or from the user interface frame. + */ + type: "Fdc3UserInterfaceResolve"; +} + +/** + * The message payload + */ +export interface Fdc3UserInterfaceResolvePayload { + /** + * An array of AppIntent objects defining the resolution options. + */ + appIntents: AppIntent[]; + context: Context; +} + +/** + * An interface that relates an intent to apps + * + * Used if a raiseIntent request requires additional resolution (e.g. by showing an intent + * resolver) before it can be handled. + */ +export interface AppIntent { + /** + * Details of applications that can resolve the intent. + */ + apps: AppMetadata[]; + /** + * Details of the intent whose relationship to resolving applications is being described. + */ + intent: IntentMetadata; } /** @@ -1251,297 +1484,154 @@ export interface Image { } /** - * Constants representing the errors that can be encountered when calling the `open` method - * on the DesktopAgent object (`fdc3`). - * - * Constants representing the errors that can be encountered when calling the `findIntent`, - * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the - * DesktopAgent (`fdc3`). - * - * Unique identifier for a request or event message. Required in all message types - * - * Unique identifier for a response to a specific message and must always be accompanied by - * a RequestUuid. - * - * Unique identifier for a `listener` object returned by a Desktop Agent to an app in - * response to addContextListener, addIntentListener or one of the PrivateChannel event - * listeners and used to identify it in messages (e.g. when unsubscribing). - * - * Unique identifier for an event message sent from a Desktop Agent to an app. - * - * Unique identifier for a for an attempt to connect to a Desktop Agent. A Unique UUID - * should be used in the first (WCP1Hello) message and should be quoted in all subsequent - * messages to link them to the same connection attempt. - * - * Should be set if the raiseIntent request returned an error. - */ -export type FindInstancesErrors = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; - -/** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ - -/** - * A request for details of apps available to resolve a particular intent and context pair. + * Details of the intent whose relationship to resolving applications is being described. * - * A request message from an FDC3-enabled app to a Desktop Agent. + * Intent descriptor */ -export interface FindIntentRequest { - /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. - */ - meta: AddContextListenerRequestMeta; +export interface IntentMetadata { /** - * The message payload typically contains the arguments to FDC3 API functions. + * Display name for the intent. */ - payload: FindIntentRequestPayload; + displayName?: string; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * The unique name of the intent that can be invoked by the raiseIntent call */ - type: "findIntentRequest"; -} - -/** - * The message payload typically contains the arguments to FDC3 API functions. - */ -export interface FindIntentRequestPayload { - context?: Context; - intent: string; - resultType?: string; + name: string; } /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * Identifies the type of the message to or from the user interface frame. */ /** - * A response to a findIntent request. + * Message from an intent resolver UI to DA proxy code in getAgent() reporting a user + * action. * - * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the - * payload contains an `error` property, the request was unsuccessful. + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. */ -export interface FindIntentResponse { - /** - * Metadata for messages sent by a Desktop Agent to an App in response to an API call - */ - meta: AddContextListenerResponseMeta; +export interface Fdc3UserInterfaceResolveAction { /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. + * The message payload */ - payload: FindIntentResponsePayload; + payload: Fdc3UserInterfaceResolveActionPayload; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * Identifies the type of the message to or from the user interface frame. */ - type: "findIntentResponse"; + type: "Fdc3UserInterfaceResolveAction"; } /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. - */ -export interface FindIntentResponsePayload { - error?: FindInstancesErrors; - appIntent?: AppIntent; -} - -/** - * An interface that relates an intent to apps - * - * Used if a raiseIntent request requires additional resolution (e.g. by showing an intent - * resolver) before it can be handled. + * The message payload */ -export interface AppIntent { +export interface Fdc3UserInterfaceResolveActionPayload { + action: Action; /** - * Details of applications that can resolve the intent. + * The App resolution option chosen */ - apps: AppMetadata[]; + appIdentifier?: AppIdentifier; /** - * Details of the intent whose relationship to resolving applications is being described. + * The intent resolved */ - intent: IntentMetadata; + intent?: string; } -/** - * Details of the intent whose relationship to resolving applications is being described. - * - * Intent descriptor - */ -export interface IntentMetadata { - /** - * Display name for the intent. - */ - displayName?: string; - /** - * The unique name of the intent that can be invoked by the raiseIntent call - */ - name: string; -} +export type Action = "hover" | "click" | "cancel"; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * Identifies the type of the message to or from the user interface frame. */ /** - * A request for details of intents and apps available to resolve them for a particular - * context. + * Message from a UI frame to the DA proxy code (setup by `getAgent()`) with updated styling + * information to apply to it. Can be used to implement a pop-open or close interaction or + * other transition needed by a UI implementation. * - * A request message from an FDC3-enabled app to a Desktop Agent. + * A message used to communicate with user interface frames injected by `getAgent()` for + * displaying UI elements such as the intent resolver or channel selector. Used for messages + * sent in either direction. */ -export interface FindIntentsByContextRequest { +export interface Fdc3UserInterfaceRestyle { /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. - */ - meta: AddContextListenerRequestMeta; - /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload */ - payload: FindIntentsByContextRequestPayload; + payload: Fdc3UserInterfaceRestylePayload; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * Identifies the type of the message to or from the user interface frame. */ - type: "findIntentsByContextRequest"; + type: "Fdc3UserInterfaceRestyle"; } /** - * The message payload typically contains the arguments to FDC3 API functions. + * The message payload */ -export interface FindIntentsByContextRequestPayload { - context: Context; - resultType?: string; +export interface Fdc3UserInterfaceRestylePayload { + /** + * A constrained set of styling properties that should be applied to the frame. Note + * `position` cannot be set, and should always be `fixed`. + */ + updatedCSS: UpdatedCSS; } /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. - */ - -/** - * A response to a findIntentsByContext request. - * - * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the - * payload contains an `error` property, the request was unsuccessful. + * A constrained set of styling properties that should be applied to the frame. Note + * `position` cannot be set, and should always be `fixed`. */ -export interface FindIntentsByContextResponse { +export interface UpdatedCSS { /** - * Metadata for messages sent by a Desktop Agent to an App in response to an API call + * The initial bottom property to apply to the iframe */ - meta: AddContextListenerResponseMeta; + bottom?: string; /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. + * The updated height of the iframe */ - payload: FindIntentsByContextResponsePayload; + height?: string; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * The initial left property to apply to the iframe */ - type: "findIntentsByContextResponse"; -} - -/** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. - */ -export interface FindIntentsByContextResponsePayload { - error?: FindInstancesErrors; - appIntents?: AppIntent[]; -} - -/** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. - */ - -/** - * A request for metadata about an app. - * - * A request message from an FDC3-enabled app to a Desktop Agent. - */ -export interface GetAppMetadataRequest { + left?: string; /** - * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + * The updated maximum height to apply to the iframe */ - meta: AddContextListenerRequestMeta; + maxHeight?: string; /** - * The message payload typically contains the arguments to FDC3 API functions. + * The updated maximum with to apply to the iframe */ - payload: GetAppMetadataRequestPayload; + maxWidth?: string; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. + * The initial right property to apply to the iframe */ - type: "getAppMetadataRequest"; -} - -/** - * The message payload typically contains the arguments to FDC3 API functions. - */ -export interface GetAppMetadataRequestPayload { - app: AppIdentifier; -} - -/** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Request' appended. - */ - -/** - * A response to a getAppMetadata request. - * - * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the - * payload contains an `error` property, the request was unsuccessful. - */ -export interface GetAppMetadataResponse { + right?: string; /** - * Metadata for messages sent by a Desktop Agent to an App in response to an API call + * The initial top property to apply to the iframe */ - meta: AddContextListenerResponseMeta; + top?: string; /** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. + * The updated transition property to apply to the iframe */ - payload: GetAppMetadataResponsePayload; + transition?: string; /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * The updated width of the iframe */ - type: "getAppMetadataResponse"; -} - -/** - * A payload for a response to an API call that will contain any return values or an `error` - * property containing a standardized error message indicating that the request was - * unsuccessful. - */ -export interface GetAppMetadataResponsePayload { - error?: FindInstancesErrors; - appMetadata?: AppMetadata; + width?: string; + /** + * The updated zindex to apply to the iframe + */ + zIndex?: string; + [property: string]: any; } /** - * Identifies the type of the message and it is typically set to the FDC3 function name that - * the message relates to, e.g. 'findIntent', with 'Response' appended. + * Identifies the type of the message to or from the user interface frame. */ /** - * A request to return the Channel object for the current User channel membership. Returns - * `null` if the app is not joined to a channel. + * A request for details of instances of a particular app. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface GetCurrentChannelRequest { +export interface FindInstancesRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1549,18 +1639,19 @@ export interface GetCurrentChannelRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: GetCurrentChannelRequestPayload; + payload: FindInstancesRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "getCurrentChannelRequest"; + type: "findInstancesRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface GetCurrentChannelRequestPayload { +export interface FindInstancesRequestPayload { + app: AppIdentifier; } /** @@ -1569,12 +1660,12 @@ export interface GetCurrentChannelRequestPayload { */ /** - * A response to a getCurrentChannel request. + * A response to a findInstances request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface GetCurrentChannelResponse { +export interface FindInstancesResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1584,37 +1675,66 @@ export interface GetCurrentChannelResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: GetCurrentChannelResponsePayload; + payload: FindInstancesResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "getCurrentChannelResponse"; + type: "findInstancesResponse"; } /** * A payload for a response to an API call that will contain any return values or an `error` * property containing a standardized error message indicating that the request was * unsuccessful. + * + * The message payload contains a flag indicating whether the API call was successful, plus + * any return values for the FDC3 API function called, or indicating that the request + * resulted in an error and including a standardized error message. */ -export interface GetCurrentChannelResponsePayload { - error?: ResponsePayloadError; - channel?: Channel | null; +export interface FindInstancesResponsePayload { + error?: FindInstancesErrors; + appIdentifiers?: AppMetadata[]; } +/** + * Constants representing the errors that can be encountered when calling the `open` method + * on the DesktopAgent object (`fdc3`). + * + * Constants representing the errors that can be encountered when calling the `findIntent`, + * `findIntentsByContext`, `raiseIntent` or `raiseIntentForContext` methods on the + * DesktopAgent (`fdc3`). + * + * Unique identifier for a request or event message. Required in all message types + * + * Unique identifier for a response to a specific message and must always be accompanied by + * a RequestUuid. + * + * Unique identifier for a `listener` object returned by a Desktop Agent to an app in + * response to addContextListener, addIntentListener or one of the PrivateChannel event + * listeners and used to identify it in messages (e.g. when unsubscribing). + * + * Unique identifier for an event message sent from a Desktop Agent to an app. + * + * Unique identifier for a for an attempt to connect to a Desktop Agent. A Unique UUID + * should be used in the first (WCP1Hello) message and should be quoted in all subsequent + * messages to link them to the same connection attempt. + * + * Should be set if the raiseIntent request returned an error. + */ +export type FindInstancesErrors = "MalformedContext" | "DesktopAgentNotFound" | "ResolverUnavailable" | "IntentDeliveryFailed" | "NoAppsFound" | "ResolverTimeout" | "TargetAppUnavailable" | "TargetInstanceUnavailable" | "UserCancelledResolution" | "AgentDisconnected" | "NotConnectedToBridge" | "ResponseToBridgeTimedOut" | "MalformedMessage"; + /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ /** - * A request to return the current context (either of a specified type or most recent - * broadcast) of a specified Channel. Returns `null` if no context (of the requested type if - * one was specified) is available in the channel. + * A request for details of apps available to resolve a particular intent and context pair. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface GetCurrentContextRequest { +export interface FindIntentRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1622,27 +1742,21 @@ export interface GetCurrentContextRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: GetCurrentContextRequestPayload; + payload: FindIntentRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "getCurrentContextRequest"; + type: "findIntentRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface GetCurrentContextRequestPayload { - /** - * The id of the channel to return the current context of - */ - channelId: string; - /** - * The type of context to return for OR `null` indicating that the most recently broadcast - * context on the channel should be returned. - */ - contextType: null | string; +export interface FindIntentRequestPayload { + context?: Context; + intent: string; + resultType?: string; } /** @@ -1651,12 +1765,12 @@ export interface GetCurrentContextRequestPayload { */ /** - * A response to a getCurrentContext request. + * A response to a findIntent request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface GetCurrentContextResponse { +export interface FindIntentResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1666,12 +1780,12 @@ export interface GetCurrentContextResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: GetCurrentContextResponsePayload; + payload: FindIntentResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "getCurrentContextResponse"; + type: "findIntentResponse"; } /** @@ -1679,13 +1793,9 @@ export interface GetCurrentContextResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface GetCurrentContextResponsePayload { - error?: PurpleError; - /** - * The most recently broadcast context object (of the specified type, if one was specified), - * or `null` if none was available in the channel - */ - context?: null | Context; +export interface FindIntentResponsePayload { + error?: FindInstancesErrors; + appIntent?: AppIntent; } /** @@ -1694,12 +1804,12 @@ export interface GetCurrentContextResponsePayload { */ /** - * Request to retrieve information about the FDC3 Desktop Agent implementation and the - * metadata of the calling application according to the desktop agent. + * A request for details of intents and apps available to resolve them for a particular + * context. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface GetInfoRequest { +export interface FindIntentsByContextRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1707,18 +1817,20 @@ export interface GetInfoRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: GetInfoRequestPayload; + payload: FindIntentsByContextRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "getInfoRequest"; + type: "findIntentsByContextRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface GetInfoRequestPayload { +export interface FindIntentsByContextRequestPayload { + context: Context; + resultType?: string; } /** @@ -1727,12 +1839,12 @@ export interface GetInfoRequestPayload { */ /** - * A response to a getInfo request. + * A response to a findIntentsByContext request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface GetInfoResponse { +export interface FindIntentsByContextResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1742,12 +1854,12 @@ export interface GetInfoResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: GetInfoResponsePayload; + payload: FindIntentsByContextResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "getInfoResponse"; + type: "findIntentsByContextResponse"; } /** @@ -1755,67 +1867,9 @@ export interface GetInfoResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface GetInfoResponsePayload { - error?: ResponsePayloadError; - implementationMetadata?: ImplementationMetadata; -} - -/** - * Implementation metadata for the Desktop Agent, which includes an appMetadata element - * containing a copy of the app's own metadata - * - * Includes Metadata for the current application. - * - * Metadata relating to the FDC3 Desktop Agent implementation and its provider. - */ -export interface ImplementationMetadata { - /** - * The calling application instance's own metadata, according to the Desktop Agent (MUST - * include at least the `appId` and `instanceId`). - */ - appMetadata: AppMetadata; - /** - * The version number of the FDC3 specification that the implementation provides. - * The string must be a numeric semver version, e.g. 1.2 or 1.2.1. - */ - fdc3Version: string; - /** - * Metadata indicating whether the Desktop Agent implements optional features of - * the Desktop Agent API. - */ - optionalFeatures: OptionalFeatures; - /** - * The name of the provider of the Desktop Agent implementation (e.g. Finsemble, Glue42, - * OpenFin etc.). - */ - provider: string; - /** - * The version of the provider of the Desktop Agent implementation (e.g. 5.3.0). - */ - providerVersion?: string; -} - -/** - * Metadata indicating whether the Desktop Agent implements optional features of - * the Desktop Agent API. - */ -export interface OptionalFeatures { - /** - * Used to indicate whether the experimental Desktop Agent Bridging - * feature is implemented by the Desktop Agent. - */ - DesktopAgentBridging: boolean; - /** - * Used to indicate whether the exposure of 'originating app metadata' for - * context and intent messages is supported by the Desktop Agent. - */ - OriginatingAppMetadata: boolean; - /** - * Used to indicate whether the optional `fdc3.joinUserChannel`, - * `fdc3.getCurrentChannel` and `fdc3.leaveCurrentChannel` are implemented by - * the Desktop Agent. - */ - UserChannelMembershipAPIs: boolean; +export interface FindIntentsByContextResponsePayload { + error?: FindInstancesErrors; + appIntents?: AppIntent[]; } /** @@ -1824,12 +1878,11 @@ export interface OptionalFeatures { */ /** - * Request to return a Channel with an auto-generated identity that is intended for private - * communication between applications. + * A request for metadata about an app. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface GetOrCreateChannelRequest { +export interface GetAppMetadataRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1837,22 +1890,19 @@ export interface GetOrCreateChannelRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: GetOrCreateChannelRequestPayload; + payload: GetAppMetadataRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "getOrCreateChannelRequest"; + type: "getAppMetadataRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface GetOrCreateChannelRequestPayload { - /** - * The id of the channel to return - */ - channelId: string; +export interface GetAppMetadataRequestPayload { + app: AppIdentifier; } /** @@ -1861,12 +1911,12 @@ export interface GetOrCreateChannelRequestPayload { */ /** - * A response to a getOrCreateChannel request. + * A response to a getAppMetadata request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface GetOrCreateChannelResponse { +export interface GetAppMetadataResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1876,12 +1926,12 @@ export interface GetOrCreateChannelResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: GetOrCreateChannelResponsePayload; + payload: GetAppMetadataResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "getOrCreateChannelResponse"; + type: "getAppMetadataResponse"; } /** @@ -1889,9 +1939,9 @@ export interface GetOrCreateChannelResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface GetOrCreateChannelResponsePayload { - error?: PurpleError; - channel?: Channel; +export interface GetAppMetadataResponsePayload { + error?: FindInstancesErrors; + appMetadata?: AppMetadata; } /** @@ -1900,11 +1950,12 @@ export interface GetOrCreateChannelResponsePayload { */ /** - * Request to retrieve a list of the User Channels available for the app to join. + * A request to return the Channel object for the current User channel membership. Returns + * `null` if the app is not joined to a channel. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface GetUserChannelsRequest { +export interface GetCurrentChannelRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1912,18 +1963,18 @@ export interface GetUserChannelsRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: GetUserChannelsRequestPayload; + payload: GetCurrentChannelRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "getUserChannelsRequest"; + type: "getCurrentChannelRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface GetUserChannelsRequestPayload { +export interface GetCurrentChannelRequestPayload { } /** @@ -1932,12 +1983,12 @@ export interface GetUserChannelsRequestPayload { */ /** - * A response to a getUserChannels request. + * A response to a getCurrentChannel request. * * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the * payload contains an `error` property, the request was unsuccessful. */ -export interface GetUserChannelsResponse { +export interface GetCurrentChannelResponse { /** * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ @@ -1947,12 +1998,12 @@ export interface GetUserChannelsResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ - payload: GetUserChannelsResponsePayload; + payload: GetCurrentChannelResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "getUserChannelsResponse"; + type: "getCurrentChannelResponse"; } /** @@ -1960,9 +2011,9 @@ export interface GetUserChannelsResponse { * property containing a standardized error message indicating that the request was * unsuccessful. */ -export interface GetUserChannelsResponsePayload { - error?: PurpleError; - userChannels?: Channel[]; +export interface GetCurrentChannelResponsePayload { + error?: ResponsePayloadError; + channel?: Channel | null; } /** @@ -1971,12 +2022,13 @@ export interface GetUserChannelsResponsePayload { */ /** - * A request that serves as an acknowledgement of a heartbeat event from the Desktop Agent - * and indicates that an application window or frame is still alive. + * A request to return the current context (either of a specified type or most recent + * broadcast) of a specified Channel. Returns `null` if no context (of the requested type if + * one was specified) is available in the channel. * * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface HeartbeatAcknowledgementRequest { +export interface GetCurrentContextRequest { /** * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ @@ -1984,22 +2036,27 @@ export interface HeartbeatAcknowledgementRequest { /** * The message payload typically contains the arguments to FDC3 API functions. */ - payload: HeartbeatAcknowledgementRequestPayload; + payload: GetCurrentContextRequestPayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "heartbeatAcknowledgementRequest"; + type: "getCurrentContextRequest"; } /** * The message payload typically contains the arguments to FDC3 API functions. */ -export interface HeartbeatAcknowledgementRequestPayload { +export interface GetCurrentContextRequestPayload { /** - * The eventUuid value of the HeartbeatEvent that the acknowledgement being sent relates to. + * The id of the channel to return the current context of */ - heartbeatEventUuid: string; + channelId: string; + /** + * The type of context to return for OR `null` indicating that the most recently broadcast + * context on the channel should be returned. + */ + contextType: null | string; } /** @@ -2008,32 +2065,41 @@ export interface HeartbeatAcknowledgementRequestPayload { */ /** - * A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is - * alive and that the application should send a heartbeatResponseRequest to the agent in - * response. + * A response to a getCurrentContext request. * - * A message from a Desktop Agent to an FDC3-enabled app representing an event. + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. */ -export interface HeartbeatEvent { +export interface GetCurrentContextResponse { /** - * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. + * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ - meta: BroadcastEventMeta; + meta: AddContextListenerResponseMeta; /** - * The message payload contains details of the event that the app is being notified about. + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ - payload: HeartbeatEventPayload; + payload: GetCurrentContextResponsePayload; /** * Identifies the type of the message and it is typically set to the FDC3 function name that * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "heartbeatEvent"; + type: "getCurrentContextResponse"; } /** - * The message payload contains details of the event that the app is being notified about. + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ -export interface HeartbeatEventPayload { +export interface GetCurrentContextResponsePayload { + error?: PurpleError; + /** + * The most recently broadcast context object (of the specified type, if one was specified), + * or `null` if none was available in the channel + */ + context?: null | Context; } /** @@ -2042,418 +2108,351 @@ export interface HeartbeatEventPayload { */ /** - * Setup message sent by the DA proxy code in getAgent() to a channel selector UI in an - * iframe with the channel definitions and current channel selection. + * Request to retrieve information about the FDC3 Desktop Agent implementation and the + * metadata of the calling application according to the desktop agent. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface IframeChannels { +export interface GetInfoRequest { /** - * The message payload + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. */ - payload: IframeChannelsPayload; + payload: GetInfoRequestPayload; /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "iframeChannels"; + type: "getInfoRequest"; } /** - * The message payload + * The message payload typically contains the arguments to FDC3 API functions. */ -export interface IframeChannelsPayload { - /** - * The id of the channel that should be currently selected, or `null` if none should be - * selected - */ - selected: null | string; - /** - * User Channel definitions - */ - userChannels: Channel[]; +export interface GetInfoRequestPayload { } /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ /** - * Message from the channel selector UI to the DA proxy sent when the channel selection - * changes. + * A response to a getInfo request. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. */ -export interface IframeChannelSelected { +export interface GetInfoResponse { /** - * The message payload + * Metadata for messages sent by a Desktop Agent to an App in response to an API call */ - payload: IframeChannelSelectedPayload; + meta: AddContextListenerResponseMeta; /** - * Identifies the type of the message to or from the iframe. + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ - type: "iframeChannelSelected"; -} - -/** - * The message payload - */ -export interface IframeChannelSelectedPayload { + payload: GetInfoResponsePayload; /** - * The id of the channel that should be currently selected, or `null` if none should be - * selected + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - selected: null | string; + type: "getInfoResponse"; } /** - * Identifies the type of the message to or from the iframe. + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ +export interface GetInfoResponsePayload { + error?: ResponsePayloadError; + implementationMetadata?: ImplementationMetadata; +} /** - * Message from a UI iframe to the DA proxy (setup by `getAgent()`) indicating that the user - * is dragging the UI to a new location and providing the offset to apply to the location. - * The DA proxy implementation should limit the location to the current bounds of the - * window's viewport + * Implementation metadata for the Desktop Agent, which includes an appMetadata element + * containing a copy of the app's own metadata + * + * Includes Metadata for the current application. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * Metadata relating to the FDC3 Desktop Agent implementation and its provider. */ -export interface IframeDrag { +export interface ImplementationMetadata { /** - * The message payload + * The calling application instance's own metadata, according to the Desktop Agent (MUST + * include at least the `appId` and `instanceId`). */ - payload: IframeDragPayload; + appMetadata: AppMetadata; /** - * Identifies the type of the message to or from the iframe. + * The version number of the FDC3 specification that the implementation provides. + * The string must be a numeric semver version, e.g. 1.2 or 1.2.1. */ - type: "iframeDrag"; -} - -/** - * The message payload - */ -export interface IframeDragPayload { + fdc3Version: string; /** - * The offset to move the frame by + * Metadata indicating whether the Desktop Agent implements optional features of + * the Desktop Agent API. */ - mouseOffsets: MouseOffsets; -} - -/** - * The offset to move the frame by - */ -export interface MouseOffsets { - x: number; - y: number; -} - -/** - * Identifies the type of the message to or from the iframe. - */ - -/** - * Handshake message sent back to an iframe from the DA proxy code (setup by `getAgent()`) - * over the `MessagePort` provide in the preceding iFrameHello message, confirming that it - * is listening to the `MessagePort` for further communication. - * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. - */ -export interface IframeHandshake { + optionalFeatures: OptionalFeatures; /** - * The message payload + * The name of the provider of the Desktop Agent implementation (e.g. Finsemble, Glue42, + * OpenFin etc.). */ - payload: IframeHandshakePayload; + provider: string; /** - * Identifies the type of the message to or from the iframe. + * The version of the provider of the Desktop Agent implementation (e.g. 5.3.0). */ - type: "iframeHandshake"; + providerVersion?: string; } /** - * The message payload + * Metadata indicating whether the Desktop Agent implements optional features of + * the Desktop Agent API. */ -export interface IframeHandshakePayload { +export interface OptionalFeatures { /** - * The version of FDC3 API that the Desktop Agent will provide support for. + * Used to indicate whether the experimental Desktop Agent Bridging + * feature is implemented by the Desktop Agent. */ - fdc3Version: string; -} - -/** - * Identifies the type of the message to or from the iframe. - */ - -/** - * Hello message sent by a UI iframe to the Desktop Agent proxy setup by `getAgent()` to - * indicate it is ready to communicate, containing initial CSS to set on the iframe and - * including an appended `MessagePort` to be used for further communication. - * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. - */ -export interface IframeHello { + DesktopAgentBridging: boolean; /** - * The message payload + * Used to indicate whether the exposure of 'originating app metadata' for + * context and intent messages is supported by the Desktop Agent. */ - payload: IframeHelloPayload; + OriginatingAppMetadata: boolean; /** - * Identifies the type of the message to or from the iframe. + * Used to indicate whether the optional `fdc3.joinUserChannel`, + * `fdc3.getCurrentChannel` and `fdc3.leaveCurrentChannel` are implemented by + * the Desktop Agent. */ - type: "iframeHello"; + UserChannelMembershipAPIs: boolean; } /** - * The message payload + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export interface IframeHelloPayload { - /** - * Details about the UI implementation in the iframe, such as vendor and version, for - * logging purposes. - */ - implementationDetails: string; - /** - * A constrained set of CSS properties that should be set on the iframe before it is - * displayed. Note `position` cannot be specified and should always be set to `fixed`. - */ - initialCSS: InitialCSS; -} /** - * A constrained set of CSS properties that should be set on the iframe before it is - * displayed. Note `position` cannot be specified and should always be set to `fixed`. + * Request to return a Channel with an auto-generated identity that is intended for private + * communication between applications. + * + * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface InitialCSS { - /** - * The initial bottom property to apply to the iframe - */ - bottom?: string; - /** - * The initial height of the iframe - */ - height?: string; - /** - * The initial left property to apply to the iframe - */ - left?: string; - /** - * The maximum height to apply to the iframe - */ - maxHeight?: string; - /** - * The maximum with to apply to the iframe - */ - maxWidth?: string; - /** - * The initial right property to apply to the iframe - */ - right?: string; - /** - * The initial top property to apply to the iframe - */ - top?: string; +export interface GetOrCreateChannelRequest { /** - * The transition property to apply to the iframe + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ - transition?: string; + meta: AddContextListenerRequestMeta; /** - * The initial width of the iframe + * The message payload typically contains the arguments to FDC3 API functions. */ - width?: string; + payload: GetOrCreateChannelRequestPayload; /** - * The initial zindex to apply to the iframe + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - zIndex?: string; - [property: string]: any; + type: "getOrCreateChannelRequest"; } /** - * Identifies the type of the message to or from the iframe. - */ - -/** - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * The message payload typically contains the arguments to FDC3 API functions. */ -export interface IframeMessage { - /** - * The message payload - */ - payload?: { [key: string]: any }; +export interface GetOrCreateChannelRequestPayload { /** - * Identifies the type of the message to or from the iframe. + * The id of the channel to return */ - type: IframeMessageType; + channelId: string; } /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ -export type IframeMessageType = "iframeHello" | "iframeHandshake" | "iframeRestyle" | "iframeDrag" | "iframeResolve" | "iframeResolveAction" | "iframeChannels" | "iframeChannelSelected"; /** - * Setup message sent by the DA proxy code in getAgent() to an intent resolver UI in an - * iframe with the resolver data to setup the UI. + * A response to a getOrCreateChannel request. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. */ -export interface IframeResolve { +export interface GetOrCreateChannelResponse { /** - * The message payload + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ - payload: IframeResolvePayload; + payload: GetOrCreateChannelResponsePayload; /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "iframeResolve"; + type: "getOrCreateChannelResponse"; } /** - * The message payload + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ -export interface IframeResolvePayload { - /** - * An array of AppIntent objects defining the resolution options. - */ - appIntents: AppIntent[]; - context: Context; +export interface GetOrCreateChannelResponsePayload { + error?: PurpleError; + channel?: Channel; } /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ /** - * Message from an intent resolver UI in an iframe to DA proxy code in getAgent() reporting - * a user action. + * Request to retrieve a list of the User Channels available for the app to join. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A request message from an FDC3-enabled app to a Desktop Agent. */ -export interface IframeResolveAction { +export interface GetUserChannelsRequest { /** - * The message payload + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. + */ + meta: AddContextListenerRequestMeta; + /** + * The message payload typically contains the arguments to FDC3 API functions. */ - payload: IframeResolveActionPayload; + payload: GetUserChannelsRequestPayload; /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - type: "iframeResolveAction"; + type: "getUserChannelsRequest"; } /** - * The message payload + * The message payload typically contains the arguments to FDC3 API functions. */ -export interface IframeResolveActionPayload { - action: Action; - /** - * The App resolution option chosen - */ - appIdentifier?: AppIdentifier; - /** - * The intent resolved - */ - intent?: string; +export interface GetUserChannelsRequestPayload { } -export type Action = "hover" | "click" | "cancel"; - /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ /** - * Message from a UI iframe to the DA proxy code (setup by `getAgent()`) with updated - * styling information to apply to the iframe. Can be used to implement a pop-open or close - * interaction or other transition needed by a UI implementation. + * A response to a getUserChannels request. * - * A message used to communicate with iframes injected by `getAgent()` for displaying UI - * elements such as the intent resolver or channel selector. Used for messages sent in - * either direction. + * A message from a Desktop Agent to an FDC3-enabled app responding to an API call. If the + * payload contains an `error` property, the request was unsuccessful. */ -export interface IframeRestyle { +export interface GetUserChannelsResponse { /** - * The message payload + * Metadata for messages sent by a Desktop Agent to an App in response to an API call + */ + meta: AddContextListenerResponseMeta; + /** + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ - payload: IframeRestylePayload; + payload: GetUserChannelsResponsePayload; /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - type: "iframeRestyle"; + type: "getUserChannelsResponse"; } /** - * The message payload + * A payload for a response to an API call that will contain any return values or an `error` + * property containing a standardized error message indicating that the request was + * unsuccessful. */ -export interface IframeRestylePayload { - /** - * A constrained set of CSS properties that should be applied to the iframe. Note `position` - * cannot be set, and should always be `fixed`. - */ - updatedCSS: UpdatedCSS; +export interface GetUserChannelsResponsePayload { + error?: PurpleError; + userChannels?: Channel[]; } /** - * A constrained set of CSS properties that should be applied to the iframe. Note `position` - * cannot be set, and should always be `fixed`. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ -export interface UpdatedCSS { - /** - * The initial bottom property to apply to the iframe - */ - bottom?: string; - /** - * The updated height of the iframe - */ - height?: string; - /** - * The initial left property to apply to the iframe - */ - left?: string; + +/** + * A request that serves as an acknowledgement of a heartbeat event from the Desktop Agent + * and indicates that an application window or frame is still alive. + * + * A request message from an FDC3-enabled app to a Desktop Agent. + */ +export interface HeartbeatAcknowledgementRequest { /** - * The updated maximum height to apply to the iframe + * Metadata for a request message sent by an FDC3-enabled app to a Desktop Agent. */ - maxHeight?: string; + meta: AddContextListenerRequestMeta; /** - * The updated maximum with to apply to the iframe + * The message payload typically contains the arguments to FDC3 API functions. */ - maxWidth?: string; + payload: HeartbeatAcknowledgementRequestPayload; /** - * The initial right property to apply to the iframe + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. */ - right?: string; + type: "heartbeatAcknowledgementRequest"; +} + +/** + * The message payload typically contains the arguments to FDC3 API functions. + */ +export interface HeartbeatAcknowledgementRequestPayload { /** - * The initial top property to apply to the iframe + * The eventUuid value of the HeartbeatEvent that the acknowledgement being sent relates to. */ - top?: string; + heartbeatEventUuid: string; +} + +/** + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Request' appended. + */ + +/** + * A heartbeat message from the Desktop Agent to an app indicating that the Desktop Agent is + * alive and that the application should send a heartbeatResponseRequest to the agent in + * response. + * + * A message from a Desktop Agent to an FDC3-enabled app representing an event. + */ +export interface HeartbeatEvent { /** - * The updated transition property to apply to the iframe + * Metadata for messages sent by a Desktop Agent to an App notifying it of an event. */ - transition?: string; + meta: BroadcastEventMeta; /** - * The updated width of the iframe + * The message payload contains details of the event that the app is being notified about. */ - width?: string; + payload: HeartbeatEventPayload; /** - * The updated zindex to apply to the iframe + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ - zIndex?: string; - [property: string]: any; + type: "heartbeatEvent"; +} + +/** + * The message payload contains details of the event that the app is being notified about. + */ +export interface HeartbeatEventPayload { } /** - * Identifies the type of the message to or from the iframe. + * Identifies the type of the message and it is typically set to the FDC3 function name that + * the message relates to, e.g. 'findIntent', with 'Response' appended. */ /** @@ -3945,68 +3944,140 @@ export class Convert { return JSON.stringify(uncast(value, r("BroadcastRequest")), null, 2); } - public static toBroadcastResponse(json: string): BroadcastResponse { - return cast(JSON.parse(json), r("BroadcastResponse")); + public static toBroadcastResponse(json: string): BroadcastResponse { + return cast(JSON.parse(json), r("BroadcastResponse")); + } + + public static broadcastResponseToJson(value: BroadcastResponse): string { + return JSON.stringify(uncast(value, r("BroadcastResponse")), null, 2); + } + + public static toChannelChangedEvent(json: string): ChannelChangedEvent { + return cast(JSON.parse(json), r("ChannelChangedEvent")); + } + + public static channelChangedEventToJson(value: ChannelChangedEvent): string { + return JSON.stringify(uncast(value, r("ChannelChangedEvent")), null, 2); + } + + public static toContextListenerUnsubscribeRequest(json: string): ContextListenerUnsubscribeRequest { + return cast(JSON.parse(json), r("ContextListenerUnsubscribeRequest")); + } + + public static contextListenerUnsubscribeRequestToJson(value: ContextListenerUnsubscribeRequest): string { + return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeRequest")), null, 2); + } + + public static toContextListenerUnsubscribeResponse(json: string): ContextListenerUnsubscribeResponse { + return cast(JSON.parse(json), r("ContextListenerUnsubscribeResponse")); + } + + public static contextListenerUnsubscribeResponseToJson(value: ContextListenerUnsubscribeResponse): string { + return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeResponse")), null, 2); + } + + public static toCreatePrivateChannelRequest(json: string): CreatePrivateChannelRequest { + return cast(JSON.parse(json), r("CreatePrivateChannelRequest")); + } + + public static createPrivateChannelRequestToJson(value: CreatePrivateChannelRequest): string { + return JSON.stringify(uncast(value, r("CreatePrivateChannelRequest")), null, 2); + } + + public static toCreatePrivateChannelResponse(json: string): CreatePrivateChannelResponse { + return cast(JSON.parse(json), r("CreatePrivateChannelResponse")); + } + + public static createPrivateChannelResponseToJson(value: CreatePrivateChannelResponse): string { + return JSON.stringify(uncast(value, r("CreatePrivateChannelResponse")), null, 2); + } + + public static toEventListenerUnsubscribeRequest(json: string): EventListenerUnsubscribeRequest { + return cast(JSON.parse(json), r("EventListenerUnsubscribeRequest")); + } + + public static eventListenerUnsubscribeRequestToJson(value: EventListenerUnsubscribeRequest): string { + return JSON.stringify(uncast(value, r("EventListenerUnsubscribeRequest")), null, 2); + } + + public static toEventListenerUnsubscribeResponse(json: string): EventListenerUnsubscribeResponse { + return cast(JSON.parse(json), r("EventListenerUnsubscribeResponse")); + } + + public static eventListenerUnsubscribeResponseToJson(value: EventListenerUnsubscribeResponse): string { + return JSON.stringify(uncast(value, r("EventListenerUnsubscribeResponse")), null, 2); + } + + public static toFdc3UserInterfaceChannels(json: string): Fdc3UserInterfaceChannels { + return cast(JSON.parse(json), r("Fdc3UserInterfaceChannels")); + } + + public static fdc3UserInterfaceChannelsToJson(value: Fdc3UserInterfaceChannels): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceChannels")), null, 2); + } + + public static toFdc3UserInterfaceChannelSelected(json: string): Fdc3UserInterfaceChannelSelected { + return cast(JSON.parse(json), r("Fdc3UserInterfaceChannelSelected")); } - public static broadcastResponseToJson(value: BroadcastResponse): string { - return JSON.stringify(uncast(value, r("BroadcastResponse")), null, 2); + public static fdc3UserInterfaceChannelSelectedToJson(value: Fdc3UserInterfaceChannelSelected): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceChannelSelected")), null, 2); } - public static toChannelChangedEvent(json: string): ChannelChangedEvent { - return cast(JSON.parse(json), r("ChannelChangedEvent")); + public static toFdc3UserInterfaceDrag(json: string): Fdc3UserInterfaceDrag { + return cast(JSON.parse(json), r("Fdc3UserInterfaceDrag")); } - public static channelChangedEventToJson(value: ChannelChangedEvent): string { - return JSON.stringify(uncast(value, r("ChannelChangedEvent")), null, 2); + public static fdc3UserInterfaceDragToJson(value: Fdc3UserInterfaceDrag): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceDrag")), null, 2); } - public static toContextListenerUnsubscribeRequest(json: string): ContextListenerUnsubscribeRequest { - return cast(JSON.parse(json), r("ContextListenerUnsubscribeRequest")); + public static toFdc3UserInterfaceHandshake(json: string): Fdc3UserInterfaceHandshake { + return cast(JSON.parse(json), r("Fdc3UserInterfaceHandshake")); } - public static contextListenerUnsubscribeRequestToJson(value: ContextListenerUnsubscribeRequest): string { - return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeRequest")), null, 2); + public static fdc3UserInterfaceHandshakeToJson(value: Fdc3UserInterfaceHandshake): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceHandshake")), null, 2); } - public static toContextListenerUnsubscribeResponse(json: string): ContextListenerUnsubscribeResponse { - return cast(JSON.parse(json), r("ContextListenerUnsubscribeResponse")); + public static toFdc3UserInterfaceHello(json: string): Fdc3UserInterfaceHello { + return cast(JSON.parse(json), r("Fdc3UserInterfaceHello")); } - public static contextListenerUnsubscribeResponseToJson(value: ContextListenerUnsubscribeResponse): string { - return JSON.stringify(uncast(value, r("ContextListenerUnsubscribeResponse")), null, 2); + public static fdc3UserInterfaceHelloToJson(value: Fdc3UserInterfaceHello): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceHello")), null, 2); } - public static toCreatePrivateChannelRequest(json: string): CreatePrivateChannelRequest { - return cast(JSON.parse(json), r("CreatePrivateChannelRequest")); + public static toFdc3UserInterfaceMessage(json: string): Fdc3UserInterfaceMessage { + return cast(JSON.parse(json), r("Fdc3UserInterfaceMessage")); } - public static createPrivateChannelRequestToJson(value: CreatePrivateChannelRequest): string { - return JSON.stringify(uncast(value, r("CreatePrivateChannelRequest")), null, 2); + public static fdc3UserInterfaceMessageToJson(value: Fdc3UserInterfaceMessage): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceMessage")), null, 2); } - public static toCreatePrivateChannelResponse(json: string): CreatePrivateChannelResponse { - return cast(JSON.parse(json), r("CreatePrivateChannelResponse")); + public static toFdc3UserInterfaceResolve(json: string): Fdc3UserInterfaceResolve { + return cast(JSON.parse(json), r("Fdc3UserInterfaceResolve")); } - public static createPrivateChannelResponseToJson(value: CreatePrivateChannelResponse): string { - return JSON.stringify(uncast(value, r("CreatePrivateChannelResponse")), null, 2); + public static fdc3UserInterfaceResolveToJson(value: Fdc3UserInterfaceResolve): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceResolve")), null, 2); } - public static toEventListenerUnsubscribeRequest(json: string): EventListenerUnsubscribeRequest { - return cast(JSON.parse(json), r("EventListenerUnsubscribeRequest")); + public static toFdc3UserInterfaceResolveAction(json: string): Fdc3UserInterfaceResolveAction { + return cast(JSON.parse(json), r("Fdc3UserInterfaceResolveAction")); } - public static eventListenerUnsubscribeRequestToJson(value: EventListenerUnsubscribeRequest): string { - return JSON.stringify(uncast(value, r("EventListenerUnsubscribeRequest")), null, 2); + public static fdc3UserInterfaceResolveActionToJson(value: Fdc3UserInterfaceResolveAction): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceResolveAction")), null, 2); } - public static toEventListenerUnsubscribeResponse(json: string): EventListenerUnsubscribeResponse { - return cast(JSON.parse(json), r("EventListenerUnsubscribeResponse")); + public static toFdc3UserInterfaceRestyle(json: string): Fdc3UserInterfaceRestyle { + return cast(JSON.parse(json), r("Fdc3UserInterfaceRestyle")); } - public static eventListenerUnsubscribeResponseToJson(value: EventListenerUnsubscribeResponse): string { - return JSON.stringify(uncast(value, r("EventListenerUnsubscribeResponse")), null, 2); + public static fdc3UserInterfaceRestyleToJson(value: Fdc3UserInterfaceRestyle): string { + return JSON.stringify(uncast(value, r("Fdc3UserInterfaceRestyle")), null, 2); } public static toFindInstancesRequest(json: string): FindInstancesRequest { @@ -4169,78 +4240,6 @@ export class Convert { return JSON.stringify(uncast(value, r("HeartbeatEvent")), null, 2); } - public static toIframeChannels(json: string): IframeChannels { - return cast(JSON.parse(json), r("IframeChannels")); - } - - public static iframeChannelsToJson(value: IframeChannels): string { - return JSON.stringify(uncast(value, r("IframeChannels")), null, 2); - } - - public static toIframeChannelSelected(json: string): IframeChannelSelected { - return cast(JSON.parse(json), r("IframeChannelSelected")); - } - - public static iframeChannelSelectedToJson(value: IframeChannelSelected): string { - return JSON.stringify(uncast(value, r("IframeChannelSelected")), null, 2); - } - - public static toIframeDrag(json: string): IframeDrag { - return cast(JSON.parse(json), r("IframeDrag")); - } - - public static iframeDragToJson(value: IframeDrag): string { - return JSON.stringify(uncast(value, r("IframeDrag")), null, 2); - } - - public static toIframeHandshake(json: string): IframeHandshake { - return cast(JSON.parse(json), r("IframeHandshake")); - } - - public static iframeHandshakeToJson(value: IframeHandshake): string { - return JSON.stringify(uncast(value, r("IframeHandshake")), null, 2); - } - - public static toIframeHello(json: string): IframeHello { - return cast(JSON.parse(json), r("IframeHello")); - } - - public static iframeHelloToJson(value: IframeHello): string { - return JSON.stringify(uncast(value, r("IframeHello")), null, 2); - } - - public static toIframeMessage(json: string): IframeMessage { - return cast(JSON.parse(json), r("IframeMessage")); - } - - public static iframeMessageToJson(value: IframeMessage): string { - return JSON.stringify(uncast(value, r("IframeMessage")), null, 2); - } - - public static toIframeResolve(json: string): IframeResolve { - return cast(JSON.parse(json), r("IframeResolve")); - } - - public static iframeResolveToJson(value: IframeResolve): string { - return JSON.stringify(uncast(value, r("IframeResolve")), null, 2); - } - - public static toIframeResolveAction(json: string): IframeResolveAction { - return cast(JSON.parse(json), r("IframeResolveAction")); - } - - public static iframeResolveActionToJson(value: IframeResolveAction): string { - return JSON.stringify(uncast(value, r("IframeResolveAction")), null, 2); - } - - public static toIframeRestyle(json: string): IframeRestyle { - return cast(JSON.parse(json), r("IframeRestyle")); - } - - public static iframeRestyleToJson(value: IframeRestyle): string { - return JSON.stringify(uncast(value, r("IframeRestyle")), null, 2); - } - public static toIntentEvent(json: string): IntentEvent { return cast(JSON.parse(json), r("IntentEvent")); } @@ -4856,22 +4855,74 @@ const typeMap: any = { { json: "payload", js: "payload", typ: r("BroadcastResponseResponsePayload") }, { json: "type", js: "type", typ: r("EventListenerUnsubscribeResponseType") }, ], false), - "FindInstancesRequest": o([ - { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, - { json: "payload", js: "payload", typ: r("FindInstancesRequestPayload") }, - { json: "type", js: "type", typ: r("FindInstancesRequestType") }, + "Fdc3UserInterfaceChannels": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceChannelsPayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceChannelsType") }, ], false), - "FindInstancesRequestPayload": o([ - { json: "app", js: "app", typ: r("AppIdentifier") }, + "Fdc3UserInterfaceChannelsPayload": o([ + { json: "selected", js: "selected", typ: u(null, "") }, + { json: "userChannels", js: "userChannels", typ: a(r("Channel")) }, ], false), - "FindInstancesResponse": o([ - { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, - { json: "payload", js: "payload", typ: r("FindInstancesResponsePayload") }, - { json: "type", js: "type", typ: r("FindInstancesResponseType") }, + "Fdc3UserInterfaceChannelSelected": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceChannelSelectedPayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceChannelSelectedType") }, ], false), - "FindInstancesResponsePayload": o([ - { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, - { json: "appIdentifiers", js: "appIdentifiers", typ: u(undefined, a(r("AppMetadata"))) }, + "Fdc3UserInterfaceChannelSelectedPayload": o([ + { json: "selected", js: "selected", typ: u(null, "") }, + ], false), + "Fdc3UserInterfaceDrag": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceDragPayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceDragType") }, + ], false), + "Fdc3UserInterfaceDragPayload": o([ + { json: "mouseOffsets", js: "mouseOffsets", typ: r("MouseOffsets") }, + ], false), + "MouseOffsets": o([ + { json: "x", js: "x", typ: 0 }, + { json: "y", js: "y", typ: 0 }, + ], false), + "Fdc3UserInterfaceHandshake": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceHandshakePayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceHandshakeType") }, + ], false), + "Fdc3UserInterfaceHandshakePayload": o([ + { json: "fdc3Version", js: "fdc3Version", typ: "" }, + ], false), + "Fdc3UserInterfaceHello": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceHelloPayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceHelloType") }, + ], false), + "Fdc3UserInterfaceHelloPayload": o([ + { json: "implementationDetails", js: "implementationDetails", typ: "" }, + { json: "initialCSS", js: "initialCSS", typ: r("InitialCSS") }, + ], false), + "InitialCSS": o([ + { json: "bottom", js: "bottom", typ: u(undefined, "") }, + { json: "height", js: "height", typ: u(undefined, "") }, + { json: "left", js: "left", typ: u(undefined, "") }, + { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, + { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, + { json: "right", js: "right", typ: u(undefined, "") }, + { json: "top", js: "top", typ: u(undefined, "") }, + { json: "transition", js: "transition", typ: u(undefined, "") }, + { json: "width", js: "width", typ: u(undefined, "") }, + { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, + ], "any"), + "Fdc3UserInterfaceMessage": o([ + { json: "payload", js: "payload", typ: u(undefined, m("any")) }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceMessageType") }, + ], false), + "Fdc3UserInterfaceResolve": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceResolvePayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceResolveType") }, + ], false), + "Fdc3UserInterfaceResolvePayload": o([ + { json: "appIntents", js: "appIntents", typ: a(r("AppIntent")) }, + { json: "context", js: "context", typ: r("Context") }, + ], false), + "AppIntent": o([ + { json: "apps", js: "apps", typ: a(r("AppMetadata")) }, + { json: "intent", js: "intent", typ: r("IntentMetadata") }, ], false), "AppMetadata": o([ { json: "appId", js: "appId", typ: "" }, @@ -4898,6 +4949,55 @@ const typeMap: any = { { json: "src", js: "src", typ: "" }, { json: "type", js: "type", typ: u(undefined, "") }, ], false), + "IntentMetadata": o([ + { json: "displayName", js: "displayName", typ: u(undefined, "") }, + { json: "name", js: "name", typ: "" }, + ], false), + "Fdc3UserInterfaceResolveAction": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceResolveActionPayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceResolveActionType") }, + ], false), + "Fdc3UserInterfaceResolveActionPayload": o([ + { json: "action", js: "action", typ: r("Action") }, + { json: "appIdentifier", js: "appIdentifier", typ: u(undefined, r("AppIdentifier")) }, + { json: "intent", js: "intent", typ: u(undefined, "") }, + ], false), + "Fdc3UserInterfaceRestyle": o([ + { json: "payload", js: "payload", typ: r("Fdc3UserInterfaceRestylePayload") }, + { json: "type", js: "type", typ: r("Fdc3UserInterfaceRestyleType") }, + ], false), + "Fdc3UserInterfaceRestylePayload": o([ + { json: "updatedCSS", js: "updatedCSS", typ: r("UpdatedCSS") }, + ], false), + "UpdatedCSS": o([ + { json: "bottom", js: "bottom", typ: u(undefined, "") }, + { json: "height", js: "height", typ: u(undefined, "") }, + { json: "left", js: "left", typ: u(undefined, "") }, + { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, + { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, + { json: "right", js: "right", typ: u(undefined, "") }, + { json: "top", js: "top", typ: u(undefined, "") }, + { json: "transition", js: "transition", typ: u(undefined, "") }, + { json: "width", js: "width", typ: u(undefined, "") }, + { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, + ], "any"), + "FindInstancesRequest": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, + { json: "payload", js: "payload", typ: r("FindInstancesRequestPayload") }, + { json: "type", js: "type", typ: r("FindInstancesRequestType") }, + ], false), + "FindInstancesRequestPayload": o([ + { json: "app", js: "app", typ: r("AppIdentifier") }, + ], false), + "FindInstancesResponse": o([ + { json: "meta", js: "meta", typ: r("AddContextListenerResponseMeta") }, + { json: "payload", js: "payload", typ: r("FindInstancesResponsePayload") }, + { json: "type", js: "type", typ: r("FindInstancesResponseType") }, + ], false), + "FindInstancesResponsePayload": o([ + { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, + { json: "appIdentifiers", js: "appIdentifiers", typ: u(undefined, a(r("AppMetadata"))) }, + ], false), "FindIntentRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("FindIntentRequestPayload") }, @@ -4917,14 +5017,6 @@ const typeMap: any = { { json: "error", js: "error", typ: u(undefined, r("FindInstancesErrors")) }, { json: "appIntent", js: "appIntent", typ: u(undefined, r("AppIntent")) }, ], false), - "AppIntent": o([ - { json: "apps", js: "apps", typ: a(r("AppMetadata")) }, - { json: "intent", js: "intent", typ: r("IntentMetadata") }, - ], false), - "IntentMetadata": o([ - { json: "displayName", js: "displayName", typ: u(undefined, "") }, - { json: "name", js: "name", typ: "" }, - ], false), "FindIntentsByContextRequest": o([ { json: "meta", js: "meta", typ: r("AddContextListenerRequestMeta") }, { json: "payload", js: "payload", typ: r("FindIntentsByContextRequestPayload") }, @@ -5070,99 +5162,6 @@ const typeMap: any = { ], false), "HeartbeatEventPayload": o([ ], false), - "IframeChannels": o([ - { json: "payload", js: "payload", typ: r("IframeChannelsPayload") }, - { json: "type", js: "type", typ: r("IframeChannelsType") }, - ], false), - "IframeChannelsPayload": o([ - { json: "selected", js: "selected", typ: u(null, "") }, - { json: "userChannels", js: "userChannels", typ: a(r("Channel")) }, - ], false), - "IframeChannelSelected": o([ - { json: "payload", js: "payload", typ: r("IframeChannelSelectedPayload") }, - { json: "type", js: "type", typ: r("IframeChannelSelectedType") }, - ], false), - "IframeChannelSelectedPayload": o([ - { json: "selected", js: "selected", typ: u(null, "") }, - ], false), - "IframeDrag": o([ - { json: "payload", js: "payload", typ: r("IframeDragPayload") }, - { json: "type", js: "type", typ: r("IframeDragType") }, - ], false), - "IframeDragPayload": o([ - { json: "mouseOffsets", js: "mouseOffsets", typ: r("MouseOffsets") }, - ], false), - "MouseOffsets": o([ - { json: "x", js: "x", typ: 0 }, - { json: "y", js: "y", typ: 0 }, - ], false), - "IframeHandshake": o([ - { json: "payload", js: "payload", typ: r("IframeHandshakePayload") }, - { json: "type", js: "type", typ: r("IframeHandshakeType") }, - ], false), - "IframeHandshakePayload": o([ - { json: "fdc3Version", js: "fdc3Version", typ: "" }, - ], false), - "IframeHello": o([ - { json: "payload", js: "payload", typ: r("IframeHelloPayload") }, - { json: "type", js: "type", typ: r("IframeHelloType") }, - ], false), - "IframeHelloPayload": o([ - { json: "implementationDetails", js: "implementationDetails", typ: "" }, - { json: "initialCSS", js: "initialCSS", typ: r("InitialCSS") }, - ], false), - "InitialCSS": o([ - { json: "bottom", js: "bottom", typ: u(undefined, "") }, - { json: "height", js: "height", typ: u(undefined, "") }, - { json: "left", js: "left", typ: u(undefined, "") }, - { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, - { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, - { json: "right", js: "right", typ: u(undefined, "") }, - { json: "top", js: "top", typ: u(undefined, "") }, - { json: "transition", js: "transition", typ: u(undefined, "") }, - { json: "width", js: "width", typ: u(undefined, "") }, - { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, - ], "any"), - "IframeMessage": o([ - { json: "payload", js: "payload", typ: u(undefined, m("any")) }, - { json: "type", js: "type", typ: r("IframeMessageType") }, - ], false), - "IframeResolve": o([ - { json: "payload", js: "payload", typ: r("IframeResolvePayload") }, - { json: "type", js: "type", typ: r("IframeResolveType") }, - ], false), - "IframeResolvePayload": o([ - { json: "appIntents", js: "appIntents", typ: a(r("AppIntent")) }, - { json: "context", js: "context", typ: r("Context") }, - ], false), - "IframeResolveAction": o([ - { json: "payload", js: "payload", typ: r("IframeResolveActionPayload") }, - { json: "type", js: "type", typ: r("IframeResolveActionType") }, - ], false), - "IframeResolveActionPayload": o([ - { json: "action", js: "action", typ: r("Action") }, - { json: "appIdentifier", js: "appIdentifier", typ: u(undefined, r("AppIdentifier")) }, - { json: "intent", js: "intent", typ: u(undefined, "") }, - ], false), - "IframeRestyle": o([ - { json: "payload", js: "payload", typ: r("IframeRestylePayload") }, - { json: "type", js: "type", typ: r("IframeRestyleType") }, - ], false), - "IframeRestylePayload": o([ - { json: "updatedCSS", js: "updatedCSS", typ: r("UpdatedCSS") }, - ], false), - "UpdatedCSS": o([ - { json: "bottom", js: "bottom", typ: u(undefined, "") }, - { json: "height", js: "height", typ: u(undefined, "") }, - { json: "left", js: "left", typ: u(undefined, "") }, - { json: "maxHeight", js: "maxHeight", typ: u(undefined, "") }, - { json: "maxWidth", js: "maxWidth", typ: u(undefined, "") }, - { json: "right", js: "right", typ: u(undefined, "") }, - { json: "top", js: "top", typ: u(undefined, "") }, - { json: "transition", js: "transition", typ: u(undefined, "") }, - { json: "width", js: "width", typ: u(undefined, "") }, - { json: "zIndex", js: "zIndex", typ: u(undefined, "") }, - ], "any"), "IntentEvent": o([ { json: "meta", js: "meta", typ: r("BroadcastEventMeta") }, { json: "payload", js: "payload", typ: r("IntentEventPayload") }, @@ -5624,6 +5623,45 @@ const typeMap: any = { "EventListenerUnsubscribeResponseType": [ "eventListenerUnsubscribeResponse", ], + "Fdc3UserInterfaceChannelsType": [ + "Fdc3UserInterfaceChannels", + ], + "Fdc3UserInterfaceChannelSelectedType": [ + "Fdc3UserInterfaceChannelSelected", + ], + "Fdc3UserInterfaceDragType": [ + "Fdc3UserInterfaceDrag", + ], + "Fdc3UserInterfaceHandshakeType": [ + "Fdc3UserInterfaceHandshake", + ], + "Fdc3UserInterfaceHelloType": [ + "Fdc3UserInterfaceHello", + ], + "Fdc3UserInterfaceMessageType": [ + "Fdc3UserInterfaceChannelSelected", + "Fdc3UserInterfaceChannels", + "Fdc3UserInterfaceDrag", + "Fdc3UserInterfaceHandshake", + "Fdc3UserInterfaceHello", + "Fdc3UserInterfaceResolve", + "Fdc3UserInterfaceResolveAction", + "Fdc3UserInterfaceRestyle", + ], + "Fdc3UserInterfaceResolveType": [ + "Fdc3UserInterfaceResolve", + ], + "Action": [ + "cancel", + "click", + "hover", + ], + "Fdc3UserInterfaceResolveActionType": [ + "Fdc3UserInterfaceResolveAction", + ], + "Fdc3UserInterfaceRestyleType": [ + "Fdc3UserInterfaceRestyle", + ], "FindInstancesRequestType": [ "findInstancesRequest", ], @@ -5699,45 +5737,6 @@ const typeMap: any = { "HeartbeatEventType": [ "heartbeatEvent", ], - "IframeChannelsType": [ - "iframeChannels", - ], - "IframeChannelSelectedType": [ - "iframeChannelSelected", - ], - "IframeDragType": [ - "iframeDrag", - ], - "IframeHandshakeType": [ - "iframeHandshake", - ], - "IframeHelloType": [ - "iframeHello", - ], - "IframeMessageType": [ - "iframeChannelSelected", - "iframeChannels", - "iframeDrag", - "iframeHandshake", - "iframeHello", - "iframeResolve", - "iframeResolveAction", - "iframeRestyle", - ], - "IframeResolveType": [ - "iframeResolve", - ], - "Action": [ - "cancel", - "click", - "hover", - ], - "IframeResolveActionType": [ - "iframeResolveAction", - ], - "IframeRestyleType": [ - "iframeRestyle", - ], "IntentEventType": [ "intentEvent", ], From 0d2c9a4945b4b4d3b77bc67513777ac0295b892e Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 17:29:06 +0100 Subject: [PATCH 133/152] correcting some capitalization --- docs/api/specs/desktopAgentCommunicationProtocol.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 7588f6e9f..00485e4ee 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -448,10 +448,10 @@ Where injected user interfaces are used, standardized messaging is needed to com Messages are also provided that are specific to each interface type provided by a Desktop Agent. The following messages are specific to Channel Selector user interfaces: -- [`fdc3UserInterfaceChannels`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `fdc3UserInterfaceHandshake` and before making the injected iframe visible. -- [`fdc3UserInterfaceChannelSelected`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. +- [`Fdc3UserInterfaceChannels`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `fdc3UserInterfaceHandshake` and before making the injected iframe visible. +- [`Fdc3UserInterfaceChannelSelected`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. Messages specific to Intent Resolver user interfaces: - [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. -- [`fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. +- [`Fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. From 2c89b4d879c737577acc81b8bdc8adc2dccd93ca Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 24 Sep 2024 18:06:21 +0100 Subject: [PATCH 134/152] adjusting language in WCP1 and WCP3 field descriptions relating to user interfaces --- schemas/api/WCP1Hello.schema.json | 4 ++-- schemas/api/WCP3Handshake.schema.json | 4 ++-- src/api/BrowserTypes.ts | 19 ++++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/schemas/api/WCP1Hello.schema.json b/schemas/api/WCP1Hello.schema.json index 66a85f357..452193d1f 100644 --- a/schemas/api/WCP1Hello.schema.json +++ b/schemas/api/WCP1Hello.schema.json @@ -43,12 +43,12 @@ }, "intentResolver": { "title": "Intent Resolver Required", - "description": "A flag that may be used to indicate that an intent resolver is or is not required. Set to false if no intents, or only targeted intents, are raised", + "description": "A flag that may be used to indicate that an intent resolver is or is not required. Set to `false` if no intents, or only targeted intents, are raised.", "type": "boolean" }, "channelSelector": { "title": "Channel Selector Required", - "description": "A flag that may be used to indicate that a channel selector UI is or is not required. If the app includes its own UI for displaying ", + "description": "A flag that may be used to indicate that a channel selector user interface is or is not required. Set to `false` if the app includes its own interface for selecting channels or does not work with user channels.", "type": "boolean" } }, diff --git a/schemas/api/WCP3Handshake.schema.json b/schemas/api/WCP3Handshake.schema.json index 02c25499a..cfc829095 100644 --- a/schemas/api/WCP3Handshake.schema.json +++ b/schemas/api/WCP3Handshake.schema.json @@ -31,7 +31,7 @@ }, "intentResolverUrl": { "title": "Resolver URL", - "description": "Indicates whether an intent resolver UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent will handle another way)", + "description": "Indicates whether an intent resolver user interface is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent will handle it another way).", "oneOf": [ { "type": "string", @@ -44,7 +44,7 @@ }, "channelSelectorUrl": { "title": "Channel Selector URL", - "description": "Indicates whether a channel selector UI is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the channel selector (as the Desktop Agent will handle another way)", + "description": "Indicates whether a channel selector user interface is required and the URL to use to do so. Set to `true` to use the default or `false` to disable the channel selector (as the Desktop Agent will handle it another way).", "oneOf": [ { "type": "string", diff --git a/src/api/BrowserTypes.ts b/src/api/BrowserTypes.ts index dc99c2946..36c6ff536 100644 --- a/src/api/BrowserTypes.ts +++ b/src/api/BrowserTypes.ts @@ -3545,8 +3545,9 @@ export interface WebConnectionProtocol1HelloPayload { */ actualUrl: string; /** - * A flag that may be used to indicate that a channel selector UI is or is not required. If - * the app includes its own UI for displaying + * A flag that may be used to indicate that a channel selector user interface is or is not + * required. Set to `false` if the app includes its own interface for selecting channels or + * does not work with user channels. */ channelSelector?: boolean; /** @@ -3560,7 +3561,7 @@ export interface WebConnectionProtocol1HelloPayload { identityUrl: string; /** * A flag that may be used to indicate that an intent resolver is or is not required. Set to - * false if no intents, or only targeted intents, are raised + * `false` if no intents, or only targeted intents, are raised. */ intentResolver?: boolean; [property: string]: any; @@ -3637,9 +3638,9 @@ export interface WebConnectionProtocol3Handshake { */ export interface WebConnectionProtocol3HandshakePayload { /** - * Indicates whether a channel selector UI is required and the URL to use to do so. Set to - * `true` to use the default or `false` to disable the channel selector (as the Desktop - * Agent will handle another way) + * Indicates whether a channel selector user interface is required and the URL to use to do + * so. Set to `true` to use the default or `false` to disable the channel selector (as the + * Desktop Agent will handle it another way). */ channelSelectorUrl: boolean | string; /** @@ -3647,9 +3648,9 @@ export interface WebConnectionProtocol3HandshakePayload { */ fdc3Version: string; /** - * Indicates whether an intent resolver UI is required and the URL to use to do so. Set to - * `true` to use the default or `false` to disable the intent resolver (as the Desktop Agent - * will handle another way) + * Indicates whether an intent resolver user interface is required and the URL to use to do + * so. Set to `true` to use the default or `false` to disable the intent resolver (as the + * Desktop Agent will handle it another way). */ intentResolverUrl: boolean | string; } From be131b655bbc4812c9cfec080fb8ee86ac81cb85 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 10:45:59 +0100 Subject: [PATCH 135/152] Fixes from @novavi's review --- docs/api/ref/GetAgent.md | 83 +++--- docs/api/spec.md | 53 ++-- .../desktopAgentCommunicationProtocol.md | 16 +- docs/api/specs/webConnectionProtocol.md | 8 +- docs/api/supported-platforms.md | 16 +- ...eartbeatAcknowledgmentRequest.schema.json} | 2 +- src/api/GetAgent.ts | 281 +++++++++--------- 7 files changed, 236 insertions(+), 223 deletions(-) rename schemas/api/{heartbeatAcknowledgment.schema.json => heartbeatAcknowledgmentRequest.schema.json} (97%) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index c99283c92..8b2e26265 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -13,7 +13,7 @@ The `getAgent()` function is the recommended way for **web applications** to con ```ts -import { getAgent, DesktopAgent } from "@finos/fdc3"; +import { getAgent, DesktopAgent, AgentError } from "@finos/fdc3"; try { const desktopAgent: DesktopAgent = await getAgent(); @@ -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: @@ -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, @@ -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 @@ -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. ::: @@ -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 diff --git a/docs/api/spec.md b/docs/api/spec.md index 76b8dae24..b65e67e37 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -182,7 +182,7 @@ Since version 1.2 of the FDC3 Standard it may do so via the [`fdc3.getInfo()`](r ```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); @@ -207,8 +207,8 @@ The [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object retur ```ts -let implementationMetadata = await fdc3.getInfo(); -let {appId, instanceId} = implementationMetadata.appMetadata; +const implementationMetadata = await fdc3.getInfo(); +const { appId, instanceId } = implementationMetadata.appMetadata; ``` @@ -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 @@ -383,7 +391,7 @@ For example, to raise a specific intent: try { const resolution = await fdc3.raiseIntent("StageOrder", context); } -catch (err){ ... } +catch (err: ResolveError) { ... } ``` @@ -411,8 +419,7 @@ try { if (resolution.data) { const orderId = resolution.data.id; } -} -catch (err){ ... } +} catch (err: ResolveError) { ... } ``` @@ -446,7 +453,7 @@ try { //some time later await agent.raiseIntent("UpdateOrder", context, resolution.source); } -catch (err) { ... } +catch (err: ResolveError) { ... } ``` @@ -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}`); } ``` diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 00485e4ee..baf875be4 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -1,14 +1,12 @@ --- id: desktopAgentCommunicationProtocol sidebar_label: Desktop Agent Communication Protocol -title: Desktop Agent Communication Protocol (next) +title: Desktop Agent Communication Protocol (next) --- -# Desktop Agent Communication Protocol (DACP) - :::info _[@experimental](../fdc3-compliance#experimental-features)_ -The Desktop Agent Communication Protocol (DACP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +FDC3's Desktop Agent Communication Protocol (DACP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. ::: @@ -23,7 +21,7 @@ DACP messages are defined in [JSON Schema](https://json-schema.org/) in the [FDC TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import { BrowserTypes } from '@finos/fdc3'; +import { BrowserTypes } from "@finos/fdc3"; ``` ::: @@ -81,7 +79,7 @@ The design of the Desktop Agent Communication Protocol is guided by the followin Hence, that design is based on the assumption that all messaging between applications passes through an entity that acts as the 'Desktop Agent' and routes those messages on to the appropriate recipients (for example a context message broadcast by an app to a channel is routed onto other apps that have added a listener to that channel, or an intent and context pair raised by an application is routed to another app chosen to resolve that intent). While implementations based on a shared bus are possible, they have not been specifically considered in the design of the DACP messages. -Further, the design of the DACP is based on the assumption that applications will interact with an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface, with the DACP used behind the scenes to support communication between the implementation of that interface and an entity acting as the Desktop Agent which is running in another process or location, necessitating the use of a 'wire protocol' for communication. For example, [Browser-Resident Desktop Agent](./browserResidentDesktopAgents) implementations use the [FDC3 Web Communication Protocol (WCP)](./webConnectionProtocol.md) to connect a 'Desktop Agent Proxy', provided by the `getAgent()` implementation in the [`@finos/fdc3` npm module], and a Desktop Agent running in another frame or window which is communicated with via the DACP. +Further, the design of the DACP is based on the assumption that applications will interact with an implementation of the [`DesktopAgent`](../ref/DesktopAgent) interface, with the DACP used behind the scenes to support communication between the implementation of that interface and an entity acting as the Desktop Agent which is running in another process or location, necessitating the use of a 'wire protocol' for communication. For example, [Browser-Resident Desktop Agent](./browserResidentDesktopAgents) implementations use the [FDC3 Web Communication Protocol (WCP)](./webConnectionProtocol.md) to connect a 'Desktop Agent Proxy', provided by the `getAgent()` implementation in the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), and a Desktop Agent running in another frame or window which is communicated with via the DACP. As a Desktop Agent is expected to act as a router for messages sent through the Desktop Agent API, the DACP provides message exchanges for the registration and un-registration of listeners for particular message types (e.g. events, contexts broadcast on user channels, contexts broadcast on other channel types, raised intents etc.). In most cases, apps can register multiple listeners for the same messages (often filtered for different context or event types). However, where multiple listeners are present, only a single DACP message should be sent representing the action taken in the FDC3 API (e.g. broadcasting a message to a channel) and any multiplexing to multiple listeners should be applied at the receiving end. For example, when working with the WCP, this should be handled by the Desktop Agent Proxy implementation provided by the `getAgent()` implementation. @@ -446,12 +444,12 @@ Where injected user interfaces are used, standardized messaging is needed to com - [`Fdc3UserInterfaceDrag`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceDrag.schema.json): Message sent by the iframe to indicate that it is being dragged to a new position and including offsets to indicate direction and distance. - [`Fdc3UserInterfaceRestyle`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceRestyle.schema.json): Message sent by the iframe to indicate that its frame should have updated CSS applied to it, for example to support a channel selector interface that can be 'popped open' or an intent resolver that wishes to resize itself to show additional content. -Messages are also provided that are specific to each interface type provided by a Desktop Agent. The following messages are specific to Channel Selector user interfaces: +Messages are also provided that are specific to each user interface type provided by a Desktop Agent. The following messages are specific to Channel Selector user interfaces: - [`Fdc3UserInterfaceChannels`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannels.schema.json): Sent by the parent frame to initialize a Channel Selector user interface by providing metadata for the Desktop Agent's user channels and details of any channel that is already selected. This message will typically be sent by a `getAgent()` implementation immediately after the `fdc3UserInterfaceHandshake` and before making the injected iframe visible. - [`Fdc3UserInterfaceChannelSelected`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceChannelSelected.schema.json): Sent by the Channel Selector to indicate that a channel has been selected or deselected. Messages specific to Intent Resolver user interfaces: -- [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making it visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. -- [`Fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected. +- [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making the iframe visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. +- [`Fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected to ensure that it does not interfere with user's ongoing interaction with the app's user interface. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 31668dab9..f1062bcf7 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -1,12 +1,12 @@ --- id: webConnectionProtocol sidebar_label: Web Connection Protocol -title: FDC3 Web Connection Protocol (next) +title: Web Connection Protocol (next) --- :::info _[@experimental](../fdc3-compliance#experimental-features)_ -The FDC3 Web Connection Protocol (WCP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +FDC3's Web Connection Protocol (WCP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. ::: @@ -14,13 +14,13 @@ The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-applicati :::tip -The [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3) provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Support Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. +The [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3) provides a `getAgent()` implementation which app can use to connect to a Desktop Agent without having to interact with or understand the WCP directly. See [Supported Platforms](../supported-platforms) and the [`getAgent()`](../ref/GetAgent) reference page for more details on using `getAgent()` in an application. ::: The WCP supports both interfaces to web-based Desktop Agents defined in the FDC3 Standard: -- **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an electron (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. +- **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an [Electron](https://www.electronjs.org/) (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. - **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent implementation running in another frame or window, via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). The WCP allows FDC3-enabled application to detect which FDC3 web-interface is present and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 5e8d872b5..0112659ef 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -11,10 +11,10 @@ There are two main categories of platform: web and native, both of which are des :::tip -The recommended way to get access to the FDC3 Desktop Agent API in an application is to to import and call the `getAgent` function from the FDC3 NPM module, which supports all standard's compliant Desktop Agents for web applications: +The recommended way to get access to the FDC3 Desktop Agent API in an application is to to import and call the `getAgent` function from the FDC3 NPM module, which supports all FDC3 Standard conformant Desktop Agents for web applications: ```ts -import { DesktopAgent, getAgent } from "@finos/fdc3"; +import { DesktopAgent, getAgent, AgentError } from "@finos/fdc3"; //... @@ -97,18 +97,20 @@ Once you've retrieved a `DesktopAgent` interface, there are two main ways FDC3 c Simply use the interface you've retrieved and address the API directly: ```js +import { DesktopAgent, getAgent } from "@finos/fdc3"; + async function sendData(desktopAgent: DesktopAgent) { await desktopAgent.broadcast({ type: "fdc3.instrument", id: { ticker: "AAPL" } - }) + }); } const desktopAgent: DesktopAgent = await getAgent(); await sendData(desktopAgent); ``` -#### 2. es6-style Function Wrappers +#### 2. ES6-style Function Wrappers The npm package provides a wrapper around FDC3, allowing you to use it with ES6 import syntax: @@ -121,6 +123,12 @@ await raiseIntent("ViewAnalysis", { }); ``` +:::tip + +Please note that the wrapper functions will not work with the `dontSetWindowFdc3` parameter to `getAgent()` set to `true` as they rely on finding the `DesktopAgent` reference at `window.fdc3`. + +::: + ## Native The FDC3 Standard currently only defines language specific API bindings for JavaScript/TypeScript and .NET, but is intended to be implemented in other languages (which can make use of the [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) as a wire protocol, but need to define a suitable connection protocol, which includes a defined communication channel to do so). diff --git a/schemas/api/heartbeatAcknowledgment.schema.json b/schemas/api/heartbeatAcknowledgmentRequest.schema.json similarity index 97% rename from schemas/api/heartbeatAcknowledgment.schema.json rename to schemas/api/heartbeatAcknowledgmentRequest.schema.json index b1f0d24ac..b500ab1b0 100644 --- a/schemas/api/heartbeatAcknowledgment.schema.json +++ b/schemas/api/heartbeatAcknowledgmentRequest.schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgement.schema.json", + "$id": "https://fdc3.finos.org/schemas/next/api/heartbeatAcknowledgementRequest.schema.json", "type": "object", "title": "HeartbeatAcknowledgement Request", "description": "A request that serves as an acknowledgement of a heartbeat event from the Desktop Agent and indicates that an application window or frame is still alive.", diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 740ec7acb..f70e8eec4 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -4,159 +4,158 @@ */ import { DesktopAgent } from './DesktopAgent'; -/** - * Function used to retrieve an FDC3 Desktop Agent API instance, which - * supports the discovery of 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). Finally, 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. - * - * @param {GetAgentParams} params Optional parameters object, which - * may include a URL to use for the app's identity, other settings - * that affect the behavior of the getAgent() function and a `failover` - * function that should be run if a Desktop Agent is not detected. - * - * @returns A promise that resolves to a DesktopAgent implementation or - * rejects with an error message from the `AgentError` enumeration if unable to - * return a Desktop Agent implementation. - * - * @example +/** + * Function used to retrieve an FDC3 Desktop Agent API instance, which + * supports the discovery of 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). Finally, 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. + * + * @param {GetAgentParams} params Optional parameters object, which + * may include a URL to use for the app's identity, other settings + * that affect the behavior of the getAgent() function and a `failover` + * function that should be run if a Desktop Agent is not detected. + * + * @returns A promise that resolves to a DesktopAgent implementation or + * rejects with an error message from the `AgentError` enumeration if unable to + * return a Desktop Agent implementation. + * + * @example * const fdc3 = await getAgent(); - * - * // OR - * - * getAgent({ - * identityUrl: "https://example.com/path?param=appName#example", - * channelSelector: false, - * intentResolver: false - * }).then((fdc3) => { + * + * // OR + * + * getAgent({ + * identityUrl: "https://example.com/path?param=appName#example", + * channelSelector: false, + * intentResolver: false + * }).then((fdc3) => { * //do FDC3 stuff here - * }; - */ -export type GetAgentType = ( - params?: GetAgentParams, -) => Promise; + * }; + */ +export type GetAgentType = (params?: GetAgentParams) => Promise; -/** - * @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} 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 {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). If not set will - * default to true. MAY be ignored by Desktop Agent Preload (container) - * implementations. - * - * @property {boolean} intentResolver Flag indicating that the application - * needs access to an intent resolver UI (i.e. because it supports raising on or - * more intents and and does not implement its own UI for selecting target apps. - * If not set will default 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. - */ -type GetAgentParams = { - timeout?: number, - identityUrl?: string, - channelSelector?: boolean, - intentResolver?: boolean, - dontSetWindowFdc3?: boolean, - failover?: (args: GetAgentParams) => Promise -}; +/** + * @typedef {Object} GetAgentParams Type representing parameters passed to the + * getAgent function. + * + * @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 {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} 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). 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. 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 = { + timeoutMs?: number; + identityUrl?: string; + channelSelector?: boolean; + intentResolver?: boolean; + dontSetWindowFdc3?: boolean; + failover?: (args: GetAgentParams) => Promise; +}; -/** Type representing data on the Desktop Agent that an app - * connected to that is persisted by the getAgent function - * to be used when re-connecting (after a navigation or - * refresh event) and to ensure a consistent instanceId. - */ -export type DesktopAgentDetails = { - /** The type of Desktop Agent connected to. Used to - * prevent an inadvertent switch to a different agent.*/ - agentType: WebDesktopAgentType, +/** Type representing data on the Desktop Agent that an app + * connected to that is persisted by the getAgent function + * to be used when re-connecting (after a navigation or + * refresh event) and to ensure a consistent instanceId. + */ +export type DesktopAgentDetails = { + /** The type of Desktop Agent connected to. Used to + * prevent an inadvertent switch to a different agent.*/ + agentType: WebDesktopAgentType; - /** The URL that was previously sent to the Desktop Agent - * to establish the app's identity.*/ - identityUrl?: string, + /** The URL that was previously sent to the Desktop Agent + * to establish the app's identity.*/ + identityUrl?: string; - /** The current URL at the time of the last connection to - * a Desktop Agent.*/ - actualUrl?: string, + /** The current URL at the time of the last connection to + * a Desktop Agent.*/ + actualUrl?: string; - /** Optional URL field that should be used to store any - * URL that was used to connect to a Desktop Agent. URLs - * may have been provided by a parent window that has since - * gone away and persisting may allow re-connection in such - * cases. */ - agentUrl?: string, + /** Optional URL field that should be used to store any + * URL that was used to connect to a Desktop Agent. URLs + * may have been provided by a parent window that has since + * gone away and persisting may allow re-connection in such + * cases. */ + agentUrl?: string; - /** The appId that was identified for the application by the - * Desktop Agent.*/ - appId: string, + /** The appId that was identified for the application by the + * Desktop Agent.*/ + appId: string; - /** The instanceId that was issued to the app by the Desktop - * Agent. */ - instanceId: string, + /** The instanceId that was issued to the app by the Desktop + * Agent. */ + instanceId: string; - /** The instanceUuid that was issued to the app. This should be - * passed when connecting to the Desktop Agent to help - * identify that this app has connected before and which - * instance it is, enabling the Desktop Agent to reissue - * the same instanceId. The instanceUuid should never be shared - * with other applications and is not available through the - * FDC3 API, allowing it to be used as a shared secret with - * the Desktop Agent that issued the associated instanceId.*/ - instanceUuid: string + /** The instanceUuid that was issued to the app. This should be + * passed when connecting to the Desktop Agent to help + * identify that this app has connected before and which + * instance it is, enabling the Desktop Agent to reissue + * the same instanceId. The instanceUuid should never be shared + * with other applications and is not available through the + * FDC3 API, allowing it to be used as a shared secret with + * the Desktop Agent that issued the associated instanceId.*/ + instanceUuid: string; }; -/** Enumeration of values used to describe types of web-based -* Desktop Agent. Each 'type' refers to the means by which -* a connection to the agent is made and/or an interface to it -* received. */ -export enum WebDesktopAgentType { - /** Denotes Desktop Agents that inject the FDC3 interface - * at `window.fdc3`. */ - PRELOAD = "PRELOAD", +/** Enumeration of values used to describe types of web-based + * Desktop Agent. Each 'type' refers to the means by which + * a connection to the agent is made and/or an interface to it + * received. */ +export enum WebDesktopAgentType { + /** Denotes Desktop Agents that inject the FDC3 interface + * at `window.fdc3`. */ + PRELOAD = 'PRELOAD', - /** 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`. */ - PROXY_PARENT = "PROXY_PARENT", + /** 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`, + * `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. */ - PROXY_URL = "PROXY_URL", + /** 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 - * function that was passed by the application. */ - FAILOVER = "FAILOVER" -}; + /** Denotes a Desktop Agent that was returned by a failover + * function that was passed by the application. */ + FAILOVER = 'FAILOVER', +} From d2555563a651c70dd8c8e3d56ea2953549974ffd Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 10:58:46 +0100 Subject: [PATCH 136/152] more typos in web connection protocol --- docs/api/specs/webConnectionProtocol.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index f1062bcf7..ceb88024e 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -23,7 +23,7 @@ The WCP supports both interfaces to web-based Desktop Agents defined in the FDC3 - **Desktop Agent Preload**: An 'injected' or 'preloaded' Desktop Agent API implementation, typically provided by an [Electron](https://www.electronjs.org/) (or similar) container or browser extension, which is made available to web applications at `window.fdc3`. - **Desktop Agent Proxy**: An interface to a web-based Desktop Agent (implementation of the Desktop Agent API) that uses the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) to communicate with a Desktop Agent implementation running in another frame or window, via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). -The WCP allows FDC3-enabled application to detect which FDC3 web-interface is present and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). +The WCP allows FDC3-enabled applications to detect which FDC3 web-interface is present at runtime and returns a `DesktopAgent` interface implementation that the application can use to communicate, without the import of proprietary libraries or code. Hence, the WCP enables FDC3-enabled applications to be run within the scope of any standards compliant Desktop Agent without modification, enabling their developers to Write Once Run Anywhere (WORA). :::tip @@ -46,7 +46,7 @@ There are a number of messages defined as part of the Web Connection Protocol. D TypeScript types representing all DACP and WCP messages are generated from the JSON Schema source and can be imported from the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3): ```ts -import { BrowserTypes } from '@finos/fdc3'; +import { BrowserTypes } from "@finos/fdc3"; ``` ::: @@ -75,7 +75,7 @@ Messages defined as part of the Web Connection Protocol, which will be reference - [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) - [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) - [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) -- [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) +- [`WCP4ValidateAppIdentity`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) - [`WCP5ValidateAppIdentityFailedResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityFailedResponse.schema.json) - [`WCP5ValidateAppIdentityResponse`](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) - [`WCP6Goodbye`](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) From b2d2ef0bb3269a92fb62396f28a9007c14fd7b74 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 11:21:46 +0100 Subject: [PATCH 137/152] More fixes from review --- .../desktopAgentCommunicationProtocol.md | 2 ++ docs/api/specs/webConnectionProtocol.md | 30 +++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index baf875be4..5b2f77a20 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -307,6 +307,8 @@ An additional response message is provided for the delivery of an `IntentResult` - [`raiseIntentResultResponse`](https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json) +There is no request message to indicate a call to the `resolution.getResult()` function of `IntentResolution`. Hence, Desktop Agents should always send this additional response message to indicate the status of the intent handling functino and to deliver its result (or void if none was returned). + :::tip See [`addIntentListener`](#addintentlistener) above for details of the messages used for the resolving app to deliver the result to the Desktop Agent. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index ceb88024e..0b9e5feec 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -82,7 +82,7 @@ Messages defined as part of the Web Connection Protocol, which will be reference ## Establishing Connectivity Using the Web Connection Protocol (WCP) -The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps which must be completed within the window of the web application trying to connect, followed by an optional disconnection step. Each step may contain sub-steps. +The WCP algorithm (coordinated between the `getAgent()` implementation and Desktop Agent implementations) has four steps, followed by an optional disconnection step. Each step may contain sub-steps. 1. Locate a Desktop Agent interface 2. Validate app & instance identity @@ -98,7 +98,7 @@ Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDet Any data stored under the `FDC3-Desktop-Agent-Details` must conform to the [DesktopAgentDetails](../ref/GetAgent#persisted-connection-data) type. -Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `agentUrl` exists, indicating that the connection should be establish by loading the URL into a hidden iframe and initiating communication with that instead. +Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `agentUrl` exists, indicating that the connection should be established by loading the URL into a hidden iframe and initiating communication with that instead. If use of the persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with `AgentNotFound` error from the [`AgentError`](../ref/Errors#agenterror) enumeration. @@ -109,22 +109,12 @@ Next, attempt to discover whether Desktop Agent Preload or Desktop Agent Proxy i To discover a Desktop Agent Preload interface, check for the presence of an `fdc3` object in the global scope (i.e. `window.fdc3`). If it exists return it immediately. If it does not exist then add a listener for the global `fdc3Ready` event with the the specified timeout, e.g.: ```ts -const discoverPreloadDA = async (timeout): Promise => { +const discoverPreloadDA = async (timeoutMs: number): Promise => { return new Promise((resolve, reject) => { // if the global is already available resolve immediately if (window.fdc3) { resolve(window.fdc3); } else { - //`fdc3Ready` event listener function - const listener = () => { - clearTimeout(timeout); - if (window.fdc3) { - clearTimeout(timeout); - resolve(window.fdc3); - } else { - reject("The `fdc3Ready` event fired, but `window.fdc3` Was not set!"); - } - }; // Setup a timeout to return a rejected promise const timeout = setTimeout( () => { @@ -136,8 +126,18 @@ const discoverPreloadDA = async (timeout): Promise => { reject("Desktop Agent Preload not found!"); } }, - timeout + timeoutMs ); + //`fdc3Ready` event listener function + const listener = () => { + clearTimeout(timeout); + if (window.fdc3) { + resolve(window.fdc3); + } else { + reject("The `fdc3Ready` event fired, but `window.fdc3` Was not set!"); + } + }; + // listen for the fdc3Ready event window.addEventListener("fdc3Ready", listener, { once: true }); } @@ -149,7 +149,7 @@ To discover a Desktop Agent Proxy interface, locate all candidates for a `parent ```ts const discoverProxyCandidates = (): WindowProxy[] => { - const candidates = []; + const candidates: WindowProxy[] = []; //parent window if (!!window.opener) { candidates.push(window.opener); } From 5e8ba8b01ab5ecf86081172ff87369b1faf28540 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 11:27:17 +0100 Subject: [PATCH 138/152] more fixes from review --- docs/api/specs/webConnectionProtocol.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 0b9e5feec..a692a25b7 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -193,7 +193,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. If no response is received from any candidate, the `getAgent()` implementation MAY retry sending the `WCP1Hello` message periodically until the timeout is reached. - 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: + 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to step 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: - Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: ```ts const loadIframe = (url: string, loadedHandler: () => void): WindowProxy => { @@ -216,7 +216,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp ::: - 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have be received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. + 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have been received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. Add a listener (`port.addEventListener("message", (event) => {})`) to receive messages from the selected `candidate`, before moving on to the next stage. 6. If no candidates were found or no [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) has been received by the time that the timeout expires, then neither a Desktop Agent Preload or Desktop Agent Proxy interface has been discovered. If this occurs, the `getAgent()` implementation will run any `failover` function provided as a parameter to `getAgent()`, allowing the application to provide an alternative means of connecting to or starting up a Desktop Agent. From 62dd2b69f459b6160944f4d8d202941fb8e6ba06 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 11:50:24 +0100 Subject: [PATCH 139/152] More fixes from review --- docs/api/specs/webConnectionProtocol.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index a692a25b7..58a482bfd 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -10,7 +10,7 @@ FDC3's Web Connection Protocol (WCP) is an experimental feature added to FDC3 in ::: -The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents must implement in order to support discovery and connection via `getAgent()`. Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. +The FDC3 Web Connection Protocol (WCP) defines the procedure for a web-application to connect to an FDC3 Desktop Agent. The WCP is used to implement a [`getAgent()`](../ref/GetAgent) function in the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), which is the recommended way for web applications to connect to a Desktop Agent. This specification details how it retrieves and provides the FDC3 `DesktopAgent` interface object and requirements that Desktop Agents MUST implement in order to support discovery and connection via `getAgent()`. Please see the [`getAgent` reference document](../ref/GetAgent.md) for its TypeScript definition and related types. :::tip @@ -96,7 +96,7 @@ The WCP algorithm (coordinated between the `getAgent()` implementation and Deskt Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDetails` record. If it exists, then a navigation or refresh event may have occurred and the stored data MUST be sent when attempting to establish a connection to the DA. This ensures that the window can maintain a consistent `instanceId` between navigation or refresh events within an app. If it doesn't exist then proceed to step (2). -Any data stored under the `FDC3-Desktop-Agent-Details` must conform to the [DesktopAgentDetails](../ref/GetAgent#persisted-connection-data) type. +Any data stored under the `FDC3-Desktop-Agent-Details` MUST conform to the [DesktopAgentDetails](../ref/GetAgent#persisted-connection-data) type. Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `agentUrl` exists, indicating that the connection should be established by loading the URL into a hidden iframe and initiating communication with that instead. @@ -216,7 +216,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp ::: - 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have been received from either a candidate parent or a hidden iframe created in 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. + 5. At this stage, a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message should have been received from either a candidate parent or a hidden iframe created in step 4 above. This message MUST have a `MessagePort` appended to it, which is used for further communication with the Desktop Agent. Add a listener (`port.addEventListener("message", (event) => {})`) to receive messages from the selected `candidate`, before moving on to the next stage. 6. If no candidates were found or no [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) has been received by the time that the timeout expires, then neither a Desktop Agent Preload or Desktop Agent Proxy interface has been discovered. If this occurs, the `getAgent()` implementation will run any `failover` function provided as a parameter to `getAgent()`, allowing the application to provide an alternative means of connecting to or starting up a Desktop Agent. @@ -235,7 +235,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp ### Step 2: Validate app & instance identity -Apps and instance of them must identify themselves so that DAs can positively associate them with their corresponding AppD records and any existing instance identity. +Apps and instances of them identify themselves so that DAs can positively associate them with their corresponding AppD records and any existing instance identity. In the current FDC3 version, no identity validation procedures are provided for Desktop Agent Preload interfaces. Hence, it is the responsibility of such implementations to validate the identity of apps within their scope and to ensure that they update their own record of the identity if it changes during the life of the window hosting the application (i.e. due to a navigation event). It is expected that FDC3 will adopt identity validation procedures for Desktop Agent Preload interfaces in future. @@ -253,17 +253,17 @@ If the app identity is recognized the Desktop Agent will assign the appropriate If this instance of the application has connected to a Desktop Agent before and is reconnecting (due to a navigation or refresh event) then the optional `instanceId` and `instanceUuid` should be set in the [`WCP4ValidateAppIdentity`](/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message. The Desktop Agent MUST use these values to determine if it recognizes the app instance identity and that it was previously applied to application with the same `appId`. -An `instanceUuid` is used to validate instance identity because `instanceId` of an application is available to other apps through the FDC3 API and might be used to 'spoof' an identity. On the other hand, `instanceUuid` is only issued through the WCP to the specific app instance and is used as a shared secret to enable identity validation. However, as `SessionStorage` data maybe cloned when new windows are opened via `window.open()`, Desktop Agents MUST also compare the `WindowProxy` object (via the `==` operator) that the original `WCP1Hello` messages were received on to determine if they represent the same window. +An `instanceUuid` is used to validate instance identity because `instanceId` of an application is available to other apps through the FDC3 API and might be used to 'spoof' an identity. On the other hand, `instanceUuid` is only issued through the WCP to the specific app instance and is used as a shared secret to enable identity validation. However, as `SessionStorage` data may be cloned when new windows on the same origin are opened via `window.open()`, Desktop Agents MUST also compare the `WindowProxy` object (via the `==` operator) that the original `WCP1Hello` messages were received on to determine if they represent the same window. See the [Browser-Resident Desktop Agent Specification](./browserResidentDesktopAgents#validating-instance-identity) for further details of how instance identity is assigned. If no existing instance identity (`instanceId` and `instanceUuid`) is provided, or instance identity validation fails (as the `instanceUuid` is not known, or either the `appId` or `WindowProxy` objects don't match the previous connection), then the Desktop Agent MUST assign new `instanceId` and `instanceUuid` values. -The Desktop Agent MUST then respond with a [WCP5ValidateAppIdentityResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing the assigned `appId`, `instanceId` and `instanceUuid` values and the [`ImplementationMetadata`](../ref/Metadata#implementationmetadata) Object for the Desktop Agent. This message indicates that the Desktop Agent will accept the application and we can begin processing [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) messages relating to FDC3 API calls over the `MessagePort`. +The Desktop Agent MUST then respond with a [WCP5ValidateAppIdentityResponse](https://fdc3.finos.org/schemas/next/api/WCP5ValidateAppIdentityResponse.schema.json) message containing the assigned `appId`, `instanceId` and `instanceUuid` values and the [`ImplementationMetadata`](../ref/Metadata#implementationmetadata) object for the Desktop Agent. This message indicates that the Desktop Agent will accept the application and we can begin processing [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol) messages relating to FDC3 API calls over the `MessagePort`. ### Step 3: Persist DesktopAgentDetails to SessionStorage -Once a connection is established, and the app and instance identity determined, a `DesktopAgentDetails` record must be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key by the `getAgent()` implementation. This record includes: +Once a connection is established, and the app and instance identity determined, a `DesktopAgentDetails` record MUST be stored in SessionStorage under the `FDC3-Desktop-Agent-Details` key by the `getAgent()` implementation. This record includes: - The `identityUrl` and `actualUrl` passed to `getAgent()`. - The `appId`, `instanceId`, and `instanceUuid` assigned by the DA. @@ -290,7 +290,7 @@ Where a `DesktopAgent` or 'Desktop Agent Proxy' implementation was successfully ### Step 5: Disconnection -Desktop Agent Preload interfaces, as used in container-based Desktop Agent implementations, are usually able to track the lifecycle and current URL of windows that host web apps in their scope. Hence, this is currently no requirement nor means for an app to indicate that it is closing, rather it is the responsibility of the Desktop Agent to update its internal state when an app closes or changes identity. +Desktop Agent Preload interfaces, as used in container-based Desktop Agent implementations, are usually able to track the lifecycle and current URL of windows that host web apps in their scope. Hence, there is currently no requirement nor means for an app to indicate that it is closing, rather it is the responsibility of the Desktop Agent to update its internal state when an app closes or changes identity. However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the HTML Standard's [Page Life Cycle API](https://html.spec.whatwg.org/multipage/document-lifecycle.html#document-lifecycle) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event), [Chrome for Developers](https://developer.chrome.com/docs/web-platform/page-lifecycle-api#developer-recommendations-for-each-state)) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. From 22cb6be0a5dec0c969fb724a431d6226474e8aec Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 11:52:42 +0100 Subject: [PATCH 140/152] note on render thread crashes --- docs/api/specs/webConnectionProtocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 58a482bfd..e3658a776 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -294,7 +294,7 @@ Desktop Agent Preload interfaces, as used in container-based Desktop Agent imple However, Browser Resident Desktop Agents working with a Desktop Agent Proxy interface may have more trouble tracking child windows and frames. Hence, a specific WCP message ([WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json)) is provided for the `getAgent()` implementation to indicate that an app is disconnecting from the Desktop Agent and will not communicate further unless and until it reconnects via the WCP. The `getAgent()` implementation MUST listen for the `pagehide` event from the HTML Standard's [Page Life Cycle API](https://html.spec.whatwg.org/multipage/document-lifecycle.html#document-lifecycle) ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event), [Chrome for Developers](https://developer.chrome.com/docs/web-platform/page-lifecycle-api#developer-recommendations-for-each-state)) and send [WCP6Goodbye](https://fdc3.finos.org/schemas/next/api/WCP6Goodbye.schema.json) if it receives an event where the `persisted` property is `false`. -As it is possible for a page to close without firing this event in some circumstances, other procedures for detecting disconnection may also be used, these are described in the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects) and [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). +As it is possible for a page to close without firing this event in some circumstances (e.g. where a browser render thread crashes), other procedures for detecting disconnection may also be used, these are described in the [Browser Resident Desktop Agents specification](./browserResidentDesktopAgents#disconnects) and [Desktop Agent Communication Protocol](./desktopAgentCommunicationProtocol#checking-apps-are-alive). ### `getAgent()` Workflow Diagram From 4a9715ef012811274c493194fc4d0c7b7e4ba4ee Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 11:58:17 +0100 Subject: [PATCH 141/152] Capitalization in flow chart --- docs/api/specs/webConnectionProtocol.md | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index e3658a776..02f0e87b7 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -310,22 +310,22 @@ flowchart TB subgraph getAgent ["getAgent()"] A3["Check for DesktopAgentDetails in SessionStorage"] --> P1{"Does window.fdc3 exist?"} - P1 -->|yes|P2["stop timeout"] + P1 -->|yes|P2["Stop timeout"] P2 --> P21["Save DesktopAgentDetails to SessionStorage"] P1 -->|No|P3["Listen for fdc3Ready"] P3 --> P31["fdc3Ready event fires"] - P31 --> P32["stop timeout"] + P31 --> P32["Stop timeout"] P32 --> P33["Save DesktopAgentDetails to SessionStorage"] A3 --> B1{"Do parent refs exist?"} B1 -->|yes|B11["Send WCP1Hello to all candidates"] B11 --> B2["Receive WCP2LoadUrl"] - B2 --> B21["stop timeout"] + B2 --> B21["Stop timeout"] B21 --> B22["Create hidden iframe with URL"] - B22 --> B23["await iframe's load event"] + B22 --> B23["Await iframe's load event"] B23 --> B24["Send WCP1Hello to iframe"] B24 --> B3["Receive WCP3Handshake with MessagePort"] - B3 --> B31["stop timeout"] + B3 --> B31["Stop timeout"] B11 --> B3 B31 --> B32["Send WCP4ValidateIdentity on MessagePort"] B32 --> B321["Receive WCP5ValidateIdentityResponse"] @@ -334,7 +334,7 @@ flowchart TB B32 --> B322["Receive WCP5ValidateIdentityFailedResponse"] A3 --> T1["Set timeout"] - T1 --> T2["timeout expires"] + T1 --> T2["Timeout expires"] T2 --> T3{"Was a failover fn provided"} T3 -->|yes|T31["Run failover"] T31 --> T311{"Check failover return type"} @@ -342,13 +342,13 @@ flowchart TB T311 -->|DesktopAgent|T3112["Save DesktopAgentDetails to SessionStorage"] T3111 --> B3 end - P21 -->P22(["resolve with window.fdc3"]) - P33 -->P34(["resolve with window.fdc3"]) - B3212 --> B3213(["resolve with DesktopAgentProxy"]) - B322 --> B3221(["reject with AgentError.AccessDenied"]) - T3112 --> T31121(["resolve with DesktopAgent"]) - T3 -->|no|T32(["reject with AgentError.AgentNotFound"]) - T311 -->|other|T3113["reject with AgentError.InvalidFailover"] + P21 -->P22(["Resolve with window.fdc3"]) + P33 -->P34(["Resolve with window.fdc3"]) + B3212 --> B3213(["Resolve with DesktopAgentProxy"]) + B322 --> B3221(["Reject with AgentError.AccessDenied"]) + T3112 --> T31121(["Resolve with DesktopAgent"]) + T3 -->|no|T32(["Reject with AgentError.AgentNotFound"]) + T311 -->|other|T3113["Reject with AgentError.InvalidFailover"] ``` ## Providing Channel Selector and Intent Resolver UIs From 976af80d5ba8d831e202aabcd37d1a7886e4c930 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 Sep 2024 12:57:27 +0100 Subject: [PATCH 142/152] More fixes from review --- docs/api/ref/Errors.md | 7 +++---- docs/api/ref/GetAgent.md | 4 ++-- docs/api/specs/browserResidentDesktopAgents.md | 12 ++++++------ docs/api/specs/webConnectionProtocol.md | 12 ++++++------ docs/api/supported-platforms.md | 2 +- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 7ea8dd3e5..963b179fd 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -5,12 +5,11 @@ title: Errors import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -FDC3 API operations may sometimes result in an error, which must be returned to the caller. Errors should be returned by rejecting the promise returned by the API with a JavaScript `Error` object (or equivalent for the language of the implementation). The `Error` Object's message should be chosen from the appropriate Error enumeration below. +FDC3 API operations may sometimes result in an error that is returned to the caller. Errors MUST be returned by rejecting the promise returned by the API with a JavaScript `Error` object (or equivalent for the language of the implementation). The `Error` Object's message should be chosen from the appropriate Error enumeration below. ## `AgentError` -Contains constants representing the errors that can be encountered when calling the [`getAgent`](getAgent) function to establish connectivity to a Desktop Agent. Primarily used with web applications, but may also be used in other language -implementations. +Contains constants representing the errors that can be encountered when calling the [`getAgent`](getAgent) function to establish connectivity to a Desktop Agent. Primarily used with web applications, but may also be used in other language implementations. @@ -400,7 +399,7 @@ enum BridgingError { * (e.g. raiseIntent with an app on a remote DesktopAgent targeted), * when the local Desktop Agent is not connected to a bridge. */ NotConnectedToBridge = "NotConnectedToBridge", - + /** @experimental Returned if a message to a Bridge deviates from the schema * for that message sufficiently that it could not be processed. */ diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 8b2e26265..3806b1ce7 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -119,13 +119,13 @@ Finally, if there is still no Desktop Agent available, or an issue prevent conne ## Failover function -Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeout` field. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. +Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeoutMs` parameter. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. Example: Decreasing the timeout and providing a failover function ```js const desktopAgent = await getAgent({ - timeout: 250, + timeoutMs: 250, failover: async (params: GetAgentParams) => { // return WindowProxy | DesktopAgent } diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 2fdfb8510..15d104c94 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -81,7 +81,7 @@ As web applications may vary their URL during use, or serve multiple application - `identityUrl`: the URL to match to the app directory record. - `actualUrl`: the current URL of the application, which MUST be captured automatically by the `getAgent()` implementation. -Applications _may_ specify the `identityUrl` value as an argument to `getAgent()`. If not specified, the `getAgent()` implementation MUST use the current URL of the application. The Desktop Agent must validate that the origin of the `identityUrl` is the same as the origin of _both_ the `actualUrl` and the `WCPValidateAppIdentity` message sent over the `MessagePort`. The Desktop Agent MUST then match the URL to that of applications known to the Desktop Agent. +Applications _may_ specify the `identityUrl` value as an argument to `getAgent()`. If not specified, the `getAgent()` implementation MUST use the current URL of the application. The Desktop Agent MUST validate that the origin of the `identityUrl` is the same as the origin of _both_ the `actualUrl` and the `WCPValidateAppIdentity` message sent over the `MessagePort`. The Desktop Agent MUST then match the URL to that of applications known to the Desktop Agent. The `actualUrl` field may be used for logging and debug purposes by the Desktop Agent and it differing from the `identityUrl` indicates that the application provided an override via `getAgent()`. @@ -90,9 +90,9 @@ Owing to the fact that the different parts of a URL (origin, path, search parame For example, given an identity URL `url`, and an array of App Directory records `appDRecords`, a Desktop Agent MAY implement matching as follows: ```js -/** Return the AppD record whose URL best matches the input URL or `null` if no match is found. - * To be considered a match all elements of the AppD URL (origin, path, searchParams and hash) - * must be found in the input URL. +/** Return the AppD record whose URL best matches the input URL or `null` if no + * match is found. To be considered a match all elements of the AppD URL + * (origin, path, searchParams and hash) MUST be found in the input URL. * The best match is the AppD URL that contains the most elements from the * input URL. */ @@ -114,8 +114,8 @@ let matchUrlToAppD = (url, appDRecords) => { const matchScores = []; //score appD records based on the match of their URL to the input URL - // if any component of the appD record URL is missing from input URL, score 0 - // otherwise count the number of input elements matched + // if any component of the appD record URL is missing from input URL, + // score 0 otherwise count the number of input elements matched. appDRecords.map((record) => { //record must contain a URL if (!record.details?.url) { return; } diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 02f0e87b7..0608979a0 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -104,7 +104,7 @@ If use of the persisted data fails to establish a connection to the DA then `get #### 1.2 Desktop Agent Discovery -Next, attempt to discover whether Desktop Agent Preload or Desktop Agent Proxy interfaces are available, within a specified timeout. The optional `params.timeout` argument to `getAgent()` allows an application to specify the timeout that should be used, with a default value of 750ms. Discovery of Desktop Agent Preload or Desktop Agent Proxy interfaces should be conducted in parallel where possible and the timeout cancelled as soon as an interface is discovered. If a `DesktopAgentDetails` record was found in the previous step, limit discovery to the interface specified, or, if an `agentUrl` property was specified in the `DesktopAgentDetails` record, skip the discovery step entirely and proceed to the next step. +Next, attempt to discover whether Desktop Agent Preload or Desktop Agent Proxy interfaces are available, within a specified timeout. The optional `params.timeoutMs` argument to `getAgent()` allows an application to specify the timeout that should be used, with a default value of 750ms. Discovery of Desktop Agent Preload or Desktop Agent Proxy interfaces should be conducted in parallel where possible and the timeout cancelled as soon as an interface is discovered. If a `DesktopAgentDetails` record was found in the previous step, limit discovery to the interface specified, or, if an `agentUrl` property was specified in the `DesktopAgentDetails` record, skip the discovery step entirely and proceed to the next step. To discover a Desktop Agent Preload interface, check for the presence of an `fdc3` object in the global scope (i.e. `window.fdc3`). If it exists return it immediately. If it does not exist then add a listener for the global `fdc3Ready` event with the the specified timeout, e.g.: @@ -116,7 +116,7 @@ const discoverPreloadDA = async (timeoutMs: number): Promise => { resolve(window.fdc3); } else { // Setup a timeout to return a rejected promise - const timeout = setTimeout( + const timeoutId = setTimeout( () => { //clear the event listener to ignore a late event window.removeEventListener("fdc3Ready", listener); @@ -130,7 +130,7 @@ const discoverPreloadDA = async (timeoutMs: number): Promise => { ); //`fdc3Ready` event listener function const listener = () => { - clearTimeout(timeout); + clearTimeout(timeoutId); if (window.fdc3) { resolve(window.fdc3); } else { @@ -355,7 +355,7 @@ flowchart TB Users of FDC3 Desktop Agents often need access to UI controls that allow them to select user channels or to resolve intents that have multiple resolution options. Whilst apps can implement these UIs on their own via data and API calls provided by the `DesktopAgent` API, Desktop Agents typically provide these interfaces themselves. -However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. +However, Browser Resident Desktop Agents may have difficulty displaying user interfaces over applications for a variety of reasons (inability to inject code, lack of permissions to display popups, user gestures not following cross-origin comms, etc.), or may not (e.g. because they render applications in iframes within windows they control and can therefore display content over the iframe). The Web Connection Protocol and the `getAgent()` implementation based on it and incorporated into apps via the [`@finos/fdc3` npm module](https://www.npmjs.com/package/@finos/fdc3), is intended to help Desktop Agents deliver these UIs where necessary. ```mermaid flowchart LR @@ -376,9 +376,9 @@ flowchart LR B-->ir ``` -The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via to following messages: +The WCP allows applications to indicate to the `getAgent()` implementation whether they need the UIs (they may not need one or the other based on their usage of the FDC3 API, or because they implement UIs themselves) and for Desktop Agents to provide custom implementations of them, or defer to reference implementations provided by the FDC3 Standard. This is achieved via the following messages: - [`WCP1Hello`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json): Sent by an application and incorporating boolean `payload.intentResolver` and `payload.channelSelector` fields, which are set to `false` if either UI is not needed (defaults to `true`). - [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json): Response sent by the Desktop Agent and incorporating `payload.intentResolverUrl` and `payload.channelSelectorUrl` fields, which should be set to the URL for each UI implementation that should be loaded into an iframe to provide the UI (defaults to URLs for reference UI implementations provided by the FDC3 project), or set to `false` to indicate that the respective UI is not needed. Setting these fields to `true` will cause the `getAgent()` implementation to use its default URLs representing a reference implementation of each UI. -When UI iframes are created, the user interfaces may use the 'iframe' messages incorporated into the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol#controlling-injected-user-interfaces) to communicate with the `getAgent()` implementation and through it the Desktop Agent. +When UI iframes are created, the user interfaces may use the `Fdc3UserInterface` messages incorporated into the [Desktop Agent Communication Protocol (DACP)](./desktopAgentCommunicationProtocol#controlling-injected-user-interfaces) to communicate with the `getAgent()` implementation and through it the Desktop Agent. diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 0112659ef..171a9ecad 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -70,7 +70,7 @@ pnpm install @finos/fdc3 There are two standardized types of interface to a DA that a web application may use (which is appropriate depends on where the web application is run): - **Desktop Agent Preload**: Used where the Desktop Agent is able to inject the the `DesktopAgent` API at `window.fdc3` allowing an app to access it directly, for example in an Electron app or where a browser Browser Extension is in use. -- **Desktop Agent Proxy**: Used when running in a standard web browser (without a browser extension or similar customization). The Desktop Agent will often be running in a different window or frame to the application and must be communicated with via cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of cross-document messaging, allowing the application to work with the FDC3 API directly. +- **Desktop Agent Proxy**: Used when running in a standard web browser (without a browser extension or similar customization). The Desktop Agent will often be running in a different window or frame to the application and MUST be communicated with via cross-document messaging with `postMessage` and `MessagePorts` (see the [HTML5 Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html) for more details). A 'proxy' class implementing the Desktop Agent API is used to abstract the details of cross-document messaging, allowing the application to work with the FDC3 API directly. The FDC3 Standard defines a [Web Connection Protocol (WCP)](specs/webConnectionProtocol) that allows apps to work with either interface, by detecting which is applicable, and [Desktop Agent Communication Protocol (DACP)](../specs/desktopAgentCommunicationProtocol) that standardizes the messaging protocol used for cross-document messaging over `postMessage` and `MessagePorts` in a web browser. From a0a43c4b0c177b832401c59851091a1d1aa99d5d Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 Sep 2024 13:43:38 +0100 Subject: [PATCH 143/152] Change WebDesktopAgentType to PascalCase --- docs/api/ref/GetAgent.md | 8 ++++---- src/api/GetAgent.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 3806b1ce7..3c8e1144f 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -205,21 +205,21 @@ type DesktopAgentDetails = { enum WebDesktopAgentType { /** Denotes Desktop Agents that inject the FDC3 interface * at `window.fdc3`. */ - PRELOAD = "PRELOAD", + Preload = "PRELOAD", /** 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`, * `window.parent.opener` etc. */ - PROXY_PARENT = "PROXY_PARENT", + ProxyParent = "PROXY_PARENT", /** 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", + ProxyUrl = "PROXY_URL", /** Denotes a Desktop Agent that was returned by a failover * function that was passed by the application. */ - FAILOVER = "FAILOVER" + Failover = "FAILOVER" } ``` diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index f70e8eec4..455988154 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -142,20 +142,20 @@ export type DesktopAgentDetails = { export enum WebDesktopAgentType { /** Denotes Desktop Agents that inject the FDC3 interface * at `window.fdc3`. */ - PRELOAD = 'PRELOAD', + Preload = 'PRELOAD', /** 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`, * `window.parent.opener` etc. */ - PROXY_PARENT = 'PROXY_PARENT', + ProxyParent = 'PROXY_PARENT', /** 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', + ProxyUrl = 'PROXY_URL', /** Denotes a Desktop Agent that was returned by a failover * function that was passed by the application. */ - FAILOVER = 'FAILOVER', + Failover = 'FAILOVER', } From abc2b7f68d56bc65747ec0df53b7d7e9271b082f Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 Sep 2024 14:03:13 +0100 Subject: [PATCH 144/152] Adding warnings about CSPs and injected iframes --- docs/api/ref/GetAgent.md | 10 ++++++++++ docs/api/specs/browserResidentDesktopAgents.md | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 3c8e1144f..5ecbe6219 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -117,6 +117,16 @@ As web applications can navigate to or be navigated by users to different URLs a Finally, if there is still no Desktop Agent available, or an issue prevent connection to it, the `getAgent()` function will reject its promise with a message from the [`AgentError`](./Errors#agenterror) enumeration. +## Injected iframes for adaptors and user interfaces + +The `getAgent()` function may try to create hidden iframes within an application window in order to load either an adaptor to a Desktop Agent, or Intent Resolver and Channel Selector user interfaces when needed. The iframes are used in order to sandbox the relevant software, and are communicated with securely via the HTML Standard's Channel Messaging API ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API), [HTML Living Standard](https://html.spec.whatwg.org/multipage/web-messaging.html)). + +:::warning + +The [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) directives [frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src), [child-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/child-src) and [default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) can prevent iframes injected into an application from loading content. Where these are used, please ensure that they allow the loading of content from the domains of Desktop Agents and UI implementations that you wish to work with (including [fdc3.finos.org](https://fdc3.finos.org/) where the reference Intent Resolver and Channel Selector UIs may be laoded from). + +::: + ## Failover function Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the `timeoutMs` parameter. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out. diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 15d104c94..5a88f3751 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -64,6 +64,12 @@ Upon receiving an incoming [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/a 1. Respond with a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)). - This message indicates that `getAgent()` should create an iframe, load the provided URL (for an adaptor to the Desktop Agent) into it and then restart the connection process by sending [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/api/WCP1Hello.schema.json) to the iframe. + + :::warning + + The [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) directives [frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src), [child-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/child-src) and [default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) can prevent iframes injected into an application from loading content. Where these are used in app implementations, please advise app implementors to include domains from which the adaptor content is served. + + ::: 2. Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message from the application. - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel` to the message. @@ -232,6 +238,12 @@ The `getAgent()` implementation can facilitate the injection and management of i Desktop Agents may implement their own user interfaces for channel selection and intent resolution. The URL for each interface may be returned in the `channelSelectorUrl` and `intentResolverUrl` properties of the payload of the `WCP3Handshake` message sent by the DA during the connection sequence. Alternatively, if the Desktop Agent is able to provide these user interfaces by other means (for example DAs that render applications in iframes within a window they control may use other iframes to render these UIs) or if they app indicated that it did not need them then `channelSelectorUrl` and `intentResolverUrl` may be set to `false`. Finally, `channelSelectorUrl` and `intentResolverUrl` may be set to `true` to indicate that `getAgent()` should use the default reference implementations of these UIs provided via the website. +:::warning + +The [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) directives [frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src), [child-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/child-src) and [default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) can prevent iframes injected into an application from loading content. Where these are used in app implementations, please advise app implementors to include domains from which the UI implementations are served (including [fdc3.finos.org](https://fdc3.finos.org/) if you are working with the reference Intent Resolver and Channel Selector UIs). + +::: + User interface iframes are initially injected into the application window with CSS that prevents their display: ```css From de42a304de83404539749c2c1d6b11c46645460d Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 1 Oct 2024 09:19:48 -0400 Subject: [PATCH 145/152] Fixing casing issue in schema file names part 1 --- ...son => privateChannelAddEventListenerRequest.schema.json.temp} | 0 ...on => privateChannelAddEventListenerResponse.schema.json.temp} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename schemas/api/{privateChanneladdEventListenerRequest.schema.json => privateChannelAddEventListenerRequest.schema.json.temp} (100%) rename schemas/api/{privateChanneladdEventListenerResponse.schema.json => privateChannelAddEventListenerResponse.schema.json.temp} (100%) diff --git a/schemas/api/privateChanneladdEventListenerRequest.schema.json b/schemas/api/privateChannelAddEventListenerRequest.schema.json.temp similarity index 100% rename from schemas/api/privateChanneladdEventListenerRequest.schema.json rename to schemas/api/privateChannelAddEventListenerRequest.schema.json.temp diff --git a/schemas/api/privateChanneladdEventListenerResponse.schema.json b/schemas/api/privateChannelAddEventListenerResponse.schema.json.temp similarity index 100% rename from schemas/api/privateChanneladdEventListenerResponse.schema.json rename to schemas/api/privateChannelAddEventListenerResponse.schema.json.temp From ff4347c5b25009be8e289f08a20a77fff7250f28 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 1 Oct 2024 09:20:18 -0400 Subject: [PATCH 146/152] Fixing casing issue in schema file names part 2 --- ...son.temp => privateChannelAddEventListenerRequest.schema.json} | 0 ...on.temp => privateChannelAddEventListenerResponse.schema.json} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename schemas/api/{privateChannelAddEventListenerRequest.schema.json.temp => privateChannelAddEventListenerRequest.schema.json} (100%) rename schemas/api/{privateChannelAddEventListenerResponse.schema.json.temp => privateChannelAddEventListenerResponse.schema.json} (100%) diff --git a/schemas/api/privateChannelAddEventListenerRequest.schema.json.temp b/schemas/api/privateChannelAddEventListenerRequest.schema.json similarity index 100% rename from schemas/api/privateChannelAddEventListenerRequest.schema.json.temp rename to schemas/api/privateChannelAddEventListenerRequest.schema.json diff --git a/schemas/api/privateChannelAddEventListenerResponse.schema.json.temp b/schemas/api/privateChannelAddEventListenerResponse.schema.json similarity index 100% rename from schemas/api/privateChannelAddEventListenerResponse.schema.json.temp rename to schemas/api/privateChannelAddEventListenerResponse.schema.json From c94e6948821a466ecf0d4a09127cb05b638d00d9 Mon Sep 17 00:00:00 2001 From: Kris West Date: Tue, 1 Oct 2024 14:34:18 +0100 Subject: [PATCH 147/152] Apply suggestions from code review from @hughtroeger's review Co-authored-by: Hugh Troeger --- docs/api/ref/GetAgent.md | 8 ++++---- docs/api/spec.md | 2 +- docs/api/specs/browserResidentDesktopAgents.md | 6 +++--- docs/api/specs/desktopAgentCommunicationProtocol.md | 4 ++-- docs/api/specs/webConnectionProtocol.md | 6 +++--- docs/api/supported-platforms.md | 2 +- s2tQuicktypeUtil.js | 5 +++-- schemas/api/fdc3UserInterfaceHello.schema.json | 4 ++-- schemas/api/fdc3UserInterfaceRestyle.schema.json | 4 ++-- src/api/GetAgent.ts | 2 +- 10 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/api/ref/GetAgent.md b/docs/api/ref/GetAgent.md index 5ecbe6219..a2fd80b8e 100644 --- a/docs/api/ref/GetAgent.md +++ b/docs/api/ref/GetAgent.md @@ -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. -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). +To handle situations where no Desktop Agent is found, a failover function may be supplied by an 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: @@ -52,7 +52,7 @@ type GetAgentType = ( ) => Promise; ``` -A small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such a proprietary adaptor) to connect to one. +A small number of arguments are accepted that can affect the behavior of `getAgent` and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such as a proprietary adaptor) to connect to one. ```ts /** @@ -111,7 +111,7 @@ type GetAgentParams = { :::note -As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of apps identity is often necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). +As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of an app's identity is necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the [Web Connection Protocol (WCP)](specs/webConnectionProtocol). ::: @@ -123,7 +123,7 @@ The `getAgent()` function may try to create hidden iframes within an application :::warning -The [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) directives [frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src), [child-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/child-src) and [default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) can prevent iframes injected into an application from loading content. Where these are used, please ensure that they allow the loading of content from the domains of Desktop Agents and UI implementations that you wish to work with (including [fdc3.finos.org](https://fdc3.finos.org/) where the reference Intent Resolver and Channel Selector UIs may be laoded from). +The [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) directives [frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src), [child-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/child-src) and [default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) can prevent iframes injected into an application from loading content. Where these are used, please ensure that they allow the loading of content from the domains of Desktop Agents and UI implementations that you wish to work with (including [fdc3.finos.org](https://fdc3.finos.org/) where the reference Intent Resolver and Channel Selector UIs may be loaded from). ::: diff --git a/docs/api/spec.md b/docs/api/spec.md index b65e67e37..b15eec8ce 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -48,7 +48,7 @@ The FDC3 API specification consists of interfaces. It is expected that each Des - [`Channel`](ref/Channel) - [`PrivateChannel`](ref/PrivateChannel) - [`Listener`](ref/Types#listener) -- [Utility types](ref/Types#listener) and [Metadata Objects](ref/Metadata). +- [Utility types](ref/Types) and [Metadata Objects](ref/Metadata). The means to access the main FDC3 API interface (a `DesktopAgent` implementation) is defined separately for each language in which FDC3 is implemented. These definitions are important as they affect whether applications can be written in a vendor agnostic format so that they run under any Standards-conformant implementation. diff --git a/docs/api/specs/browserResidentDesktopAgents.md b/docs/api/specs/browserResidentDesktopAgents.md index 5a88f3751..edbd6796c 100644 --- a/docs/api/specs/browserResidentDesktopAgents.md +++ b/docs/api/specs/browserResidentDesktopAgents.md @@ -6,7 +6,7 @@ title: Browser-Resident Desktop Agents (next) :::info _[@experimental](../fdc3-compliance#experimental-features)_ -Browser Resident Desktop Agents (DAs) are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in future versions and they are exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +Browser Resident Desktop Agents (DAs) are an experimental feature added to FDC3 in 2.2. Limited aspects of their design may change in future versions and they are exempted from the FDC3 Standard's normal versioning and deprecation policies in order to facilitate any necessary change. ::: @@ -24,7 +24,7 @@ Prior to FDC3 2.2, only [Preload Desktop Agents](./preloadDesktopAgents) were su :::note -This document covers the requirements for _implementors of Browser-Resident Desktop Agents_. The `getAgent()` function that applications use to gain access to an fdc3 interface is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3). Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates or [supported platforms](../supported-platforms) for details of how to access the Desktop Agent API in an application. +This document covers the requirements for _implementors of Browser-Resident Desktop Agents_. The `getAgent()` function that applications use to gain access to an FDC3 interface is provided by the [`@finos/fdc3` NPM module](https://www.npmjs.com/package/@finos/fdc3). Many behavioral details of `getAgent()` are purposefully omitted from this document in order to reduce the required scope of understanding. Please refer to the [getAgent() specification in the FDC3 Web Connection Protocol](webConnectionProtocol.md) for information on how the client side operates or [supported platforms](../supported-platforms) for details of how to access the Desktop Agent API in an application. ::: @@ -71,7 +71,7 @@ Upon receiving an incoming [`"WCP1Hello"`](https://fdc3.finos.org/schemas/next/a ::: 2. Create a [`MessageChannel`](https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API) with two entangled `MessagePort` instances that will be used for further communication with the application. - - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to to receive and process a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message from the application. + - Before returning one of `MessagePort` instances, the DA MUST set up event listeners to receive and process a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message from the application. - To deliver the `MessagePort`, the DA MUST respond to the event's `source` window by responding with a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message (as defined in the [Web Connection Protocol](./webConnectionProtocol)) and append `port2` from the `MessageChannel` to the message. All further communication is conducted over the `MessageChannel`. The Desktop Agent should consider the newly created port to be inactive until a [`"WCP4ValidateAppIdentity"`](https://fdc3.finos.org/schemas/next/api/WCP4ValidateAppIdentity.schema.json) message is received via the `MessagePort` and successfully processed. diff --git a/docs/api/specs/desktopAgentCommunicationProtocol.md b/docs/api/specs/desktopAgentCommunicationProtocol.md index 5b2f77a20..919e97f39 100644 --- a/docs/api/specs/desktopAgentCommunicationProtocol.md +++ b/docs/api/specs/desktopAgentCommunicationProtocol.md @@ -307,7 +307,7 @@ An additional response message is provided for the delivery of an `IntentResult` - [`raiseIntentResultResponse`](https://fdc3.finos.org/schemas/next/api/raiseIntentResultResponse.schema.json) -There is no request message to indicate a call to the `resolution.getResult()` function of `IntentResolution`. Hence, Desktop Agents should always send this additional response message to indicate the status of the intent handling functino and to deliver its result (or void if none was returned). +There is no request message to indicate a call to the `resolution.getResult()` function of `IntentResolution`. Hence, Desktop Agents should always send this additional response message to indicate the status of the intent handling function and to deliver its result (or void if none was returned). :::tip @@ -453,5 +453,5 @@ Messages are also provided that are specific to each user interface type provide Messages specific to Intent Resolver user interfaces: -- [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize a Intent Resolver user interface to resolve a raised intent, before making the iframe visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. +- [`Fdc3UserInterfaceResolve`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolve.schema.json): Sent by the parent frame to initialize an Intent Resolver user interface to resolve a raised intent, before making the iframe visible. The message includes the context object sent with the intent and an array of one or more [`AppIntent`](../ref/Metadata#appintent) objects representing the resolution options for the intent ([`raiseIntent`](../ref/DesktopAgent#raiseintent)) or context ([`raiseIntentForContext`](../ref/DesktopAgent#raiseintentforcontext)) that was raised. - [`Fdc3UserInterfaceResolveAction`](https://fdc3.finos.org/schemas/next/api/fdc3UserInterfaceResolveAction.schema.json): Sent by the Intent Resolver to indicate actions taken by the user in the interface, including hovering over an option, clicking a cancel button, or selecting a resolution option. The Intent Resolver should be hidden by the `getAgent()` implementation after a resolution option is selected to ensure that it does not interfere with user's ongoing interaction with the app's user interface. diff --git a/docs/api/specs/webConnectionProtocol.md b/docs/api/specs/webConnectionProtocol.md index 0608979a0..775517679 100644 --- a/docs/api/specs/webConnectionProtocol.md +++ b/docs/api/specs/webConnectionProtocol.md @@ -6,7 +6,7 @@ title: Web Connection Protocol (next) :::info _[@experimental](../fdc3-compliance#experimental-features)_ -FDC3's Web Connection Protocol (WCP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. +FDC3's Web Connection Protocol (WCP) is an experimental feature added to FDC3 in 2.2. Limited aspects of its design may change in future versions and it is exempted from the FDC3 Standard's normal versioning and deprecation polices in order to facilitate any necessary change. ::: @@ -98,7 +98,7 @@ Check the SessionStorage key `FDC3-Desktop-Agent-Details` for a `DesktopAgentDet Any data stored under the `FDC3-Desktop-Agent-Details` MUST conform to the [DesktopAgentDetails](../ref/GetAgent#persisted-connection-data) type. -Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if a `agentUrl` exists, indicating that the connection should be established by loading the URL into a hidden iframe and initiating communication with that instead. +Existing `DesktopAgentDetails` records MUST be used to limit discovery actions (in the next step) to the same mechanism as previously used or to skip the discovery step entirely if an `agentUrl` exists, indicating that the connection should be established by loading the URL into a hidden iframe and initiating communication with that instead. If use of the persisted data fails to establish a connection to the DA then `getAgent()` should reject its promise with `AgentNotFound` error from the [`AgentError`](../ref/Errors#agenterror) enumeration. @@ -193,7 +193,7 @@ Setup a timer for specified timeout, and then for each `candidate` found, attemp Note that the `targetOrigin` is set to `*` as the origin of the Desktop Agent is not known at this point. 3. Accept the first correct response received from a candidate. Correct responses MUST correspond to either the [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) or [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) message schemas and MUST quote the same `meta.connectionAttemptUuid` value provided in the original `WCP1Hello` message. Stop the timeout when a correct response is received. If no response is received from any candidate, the `getAgent()` implementation MAY retry sending the `WCP1Hello` message periodically until the timeout is reached. - 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this this step and move on to step 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: + 4. If a [`WCP3Handshake`](https://fdc3.finos.org/schemas/next/api/WCP3Handshake.schema.json) was received in the previous step, skip this step and move on to step 5. However, If a [`WCP2LoadUrl`](https://fdc3.finos.org/schemas/next/api/WCP2LoadUrl.schema.json) was received in the previous step: - Create a hidden iframe within the page, set its URL to the URL provided by the `payload.iframeUrl` field of the message and add a handler to run when the iframe has loaded: ```ts const loadIframe = (url: string, loadedHandler: () => void): WindowProxy => { diff --git a/docs/api/supported-platforms.md b/docs/api/supported-platforms.md index 171a9ecad..1f8c1a7c2 100644 --- a/docs/api/supported-platforms.md +++ b/docs/api/supported-platforms.md @@ -38,7 +38,7 @@ getAgent().then((desktopAgent: DesktopAgent) => { ::: -For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can by used by web applications to retrieve a `DesktopAgent` interface and to provide typing. Each FDC3-compliant Desktop Agent that the application runs in, can then provide an implementation of the FDC3 API operations. +For a web application to use the FDC3 API it needs to retrieve a copy of the `DesktopAgent` API interface, which it will use to communicate with the Desktop Agent (this interface is often referred to as the `fdc3` object or the "FDC3 API"). FDC3 offers the [`@finos/fdc3` npm package](https://www.npmjs.com/package/@finos/fdc3) that can be used by web applications to retrieve a `DesktopAgent` interface and to provide typing. Each FDC3-compliant Desktop Agent that the application runs in, can then provide an implementation of the FDC3 API operations. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; diff --git a/s2tQuicktypeUtil.js b/s2tQuicktypeUtil.js index 03febea22..bd0c7a855 100644 --- a/s2tQuicktypeUtil.js +++ b/s2tQuicktypeUtil.js @@ -9,8 +9,9 @@ * to be interpreted as JSON input, rather than JSONSchema). * * Individual file arguments will be interpreted as 'additional' schema files - * that will be referenced from the other schemas and may not have top-level output - * schemas generated, while folders of files w + * that will be referenced from the other schemas and will not have top-level output + * schemas generated, while folders will be listed and the schema files they contain + * added as inputs and will have top-level types generated. * * */ diff --git a/schemas/api/fdc3UserInterfaceHello.schema.json b/schemas/api/fdc3UserInterfaceHello.schema.json index c8ad9a152..bcb873538 100644 --- a/schemas/api/fdc3UserInterfaceHello.schema.json +++ b/schemas/api/fdc3UserInterfaceHello.schema.json @@ -39,8 +39,8 @@ "zIndex": {"type": "string", "title": "zIndex", "description": "The initial zindex to apply to the iframe"}, "left": {"type": "string", "title": "left", "description": "The initial left property to apply to the iframe"}, "top": {"type": "string", "title": "top", "description": "The initial top property to apply to the iframe"}, - "bottom": {"type": "string", "title": "left", "description": "The initial bottom property to apply to the iframe"}, - "right": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, + "bottom": {"type": "string", "title": "bottom", "description": "The initial bottom property to apply to the iframe"}, + "right": {"type": "string", "title": "right", "description": "The initial right property to apply to the iframe"}, "transition": {"type": "string", "title": "transition", "description": "The transition property to apply to the iframe"}, "maxHeight": {"type": "string", "title": "maxHeight", "description": "The maximum height to apply to the iframe"}, "maxWidth": {"type": "string", "title": "maxWidth", "description": "The maximum with to apply to the iframe"} diff --git a/schemas/api/fdc3UserInterfaceRestyle.schema.json b/schemas/api/fdc3UserInterfaceRestyle.schema.json index 3f015038f..187ac1c7a 100644 --- a/schemas/api/fdc3UserInterfaceRestyle.schema.json +++ b/schemas/api/fdc3UserInterfaceRestyle.schema.json @@ -34,8 +34,8 @@ "zIndex": {"type": "string", "title": "zIndex", "description": "The updated zindex to apply to the iframe"}, "left": {"type": "string", "title": "left", "description": "The initial left property to apply to the iframe"}, "top": {"type": "string", "title": "top", "description": "The initial top property to apply to the iframe"}, - "bottom": {"type": "string", "title": "left", "description": "The initial bottom property to apply to the iframe"}, - "right": {"type": "string", "title": "top", "description": "The initial right property to apply to the iframe"}, + "bottom": {"type": "string", "title": "bottom", "description": "The initial bottom property to apply to the iframe"}, + "right": {"type": "string", "title": "right", "description": "The initial right property to apply to the iframe"}, "transition": {"type": "string", "title": "transition", "description": "The updated transition property to apply to the iframe"}, "maxHeight": {"type": "string", "title": "maxHeight", "description": "The updated maximum height to apply to the iframe"}, "maxWidth": {"type": "string", "title": "maxWidth", "description": "The updated maximum with to apply to the iframe"} diff --git a/src/api/GetAgent.ts b/src/api/GetAgent.ts index 455988154..bd5dbf800 100644 --- a/src/api/GetAgent.ts +++ b/src/api/GetAgent.ts @@ -9,7 +9,7 @@ import { DesktopAgent } from './DesktopAgent'; * supports the discovery of 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). Finally, if no Desktop Agent is found, - * a failover function may be supplied by app allowing it to start or otherwise + * a failover function may be supplied by an 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. From 12d3de5e9305fcbef8f8145fb3e69c938ad564e8 Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:45:02 +0100 Subject: [PATCH 148/152] Make window.fdc3 optional --- CHANGELOG.md | 1 + src/context/ContextTypes.ts | 97 ++++++++++++++++++------------------- src/index.ts | 2 +- 3 files changed, 49 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 997be7d89..4bdf63115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed +* `window.fdc3` is now an optional property and may or may not be defined. * `Listener.unsubscribe()` was made async (the return type was changed from `void` to `Promise`) for consistency with the rest of the API. ([#1305](https://github.com/finos/FDC3/pull/1305)) * Added reference materials and supported platforms information for FDC3 in .NET via the [finos/fdc3-dotnet](https://github.com/finos/fdc3-dotnet) project. ([#1108](https://github.com/finos/FDC3/pull/1108)) * The supported platforms page in the FDC3 documentation was moved into the API section as the information it provides all relates to FDC3 Desktop Agent API implementations. ([#1108](https://github.com/finos/FDC3/pull/1108)) diff --git a/src/context/ContextTypes.ts b/src/context/ContextTypes.ts index f1b809287..8c4f68f75 100644 --- a/src/context/ContextTypes.ts +++ b/src/context/ContextTypes.ts @@ -207,7 +207,7 @@ export interface Chart { } /** - * financial instrument that relates to the definition of this product + * A financial instrument that relates to the definition of this product * * * @@ -235,7 +235,7 @@ export interface InstrumentElement { * interoperability between disparate data sources. This is especially useful when using an * `id` field that is not globally unique. */ - market?: SearchCriteriaMarket; + market?: OrganizationMarket; /** * An optional human-readable name for the instrument */ @@ -304,7 +304,7 @@ export interface PurpleInstrumentIdentifiers { * interoperability between disparate data sources. This is especially useful when using an * `id` field that is not globally unique. */ -export interface SearchCriteriaMarket { +export interface OrganizationMarket { /** * https://www.bloomberg.com/ */ @@ -663,7 +663,7 @@ export interface ChatRoomObject { /** * Identifier(s) for the chat - currently unstandardized */ - id: { [key: string]: any }; + id: { [key: string]: string }; /** * Display name for the chat room */ @@ -702,7 +702,7 @@ export interface ChatRoom { /** * Identifier(s) for the chat - currently unstandardized */ - id: { [key: string]: any }; + id: { [key: string]: string }; /** * Display name for the chat room */ @@ -734,7 +734,7 @@ export interface ChatSearchCriteria { * * Empty search criteria can be supported to allow resetting of filters. */ - criteria: SearchCriteria[]; + criteria: Array; type: "fdc3.chat.searchCriteria"; id?: { [key: string]: any }; name?: string; @@ -742,26 +742,23 @@ export interface ChatSearchCriteria { } /** - * An individual criteria against which to match chat messages, based on an FDC3 context or - * free-text string. - * - * financial instrument that relates to the definition of this product + * A financial instrument that relates to the definition of this product * * * * A financial instrument from any asset class. * - * The contact that initiated the interaction - * - * A person contact that can be engaged with through email, calling, messaging, CMS, etc. - * * An entity that can be used when referencing private companies and other organizations * where a specific instrument is not available or desired e.g. CRM and News workflows. * * It is valid to include extra properties and metadata as part of the organization payload, * but the minimum requirement is for at least one specified identifier to be provided. + * + * The contact that initiated the interaction + * + * A person contact that can be engaged with through email, calling, messaging, CMS, etc. */ -export interface SearchCriteria { +export interface OrganizationObject { /** * Any combination of instrument identifiers can be used together to resolve ambiguity, or * for a better match. Not all applications will use the same instrument identifiers, which @@ -777,9 +774,9 @@ export interface SearchCriteria { * fields, define a property that makes it clear what the value represents. Doing so will * make interpretation easier for the developers of target applications. * - * Identifiers that relate to the Contact represented by this context - * * Identifiers for the organization, at least one must be provided. + * + * Identifiers that relate to the Contact represented by this context */ id: Identifiers; /** @@ -787,16 +784,16 @@ export interface SearchCriteria { * interoperability between disparate data sources. This is especially useful when using an * `id` field that is not globally unique. */ - market?: SearchCriteriaMarket; + market?: OrganizationMarket; /** * An optional human-readable name for the instrument * - * An optional human-readable name for the contact - * * An optional human-readable name of the organization + * + * An optional human-readable name for the contact */ name?: string; - type: SearchCriteriaType; + type: TentacledInteractionType; [property: string]: any; } @@ -815,9 +812,9 @@ export interface SearchCriteria { * fields, define a property that makes it clear what the value represents. Doing so will * make interpretation easier for the developers of target applications. * - * Identifiers that relate to the Contact represented by this context - * * Identifiers for the organization, at least one must be provided. + * + * Identifiers that relate to the Contact represented by this context */ export interface Identifiers { /** @@ -831,9 +828,9 @@ export interface Identifiers { /** * https://www.factset.com/ * - * FactSet Permanent Identifier representing the contact - * * FactSet Permanent Identifier representing the organization + * + * FactSet Permanent Identifier representing the contact */ FDS_ID?: string; /** @@ -862,10 +859,6 @@ export interface Identifiers { * Unstandardized stock tickers */ ticker?: string; - /** - * The email address for the contact - */ - email?: string; /** * The Legal Entity Identifier (LEI) is a 20-character, alpha-numeric code based on the ISO * 17442 standard developed by the International Organization for Standardization (ISO). It @@ -873,6 +866,10 @@ export interface Identifiers { * legal entities participating in financial transactions. */ LEI?: string; + /** + * The email address for the contact + */ + email?: string; [property: string]: any; } @@ -882,7 +879,7 @@ export interface Identifiers { * `interactionType` SHOULD be one of `'Instant Message'`, `'Email'`, `'Call'`, or * `'Meeting'` although other string values are permitted. */ -export type SearchCriteriaType = "fdc3.instrument" | "fdc3.contact" | "fdc3.organization"; +export type TentacledInteractionType = "fdc3.instrument" | "fdc3.organization" | "fdc3.contact"; /** * Free text to be used for a keyword search @@ -1583,7 +1580,7 @@ export interface ProductObject { */ id: { [key: string]: string }; /** - * financial instrument that relates to the definition of this product + * A financial instrument that relates to the definition of this product */ instrument?: InstrumentElement; /** @@ -1860,7 +1857,7 @@ export interface Product { */ id: { [key: string]: string }; /** - * financial instrument that relates to the definition of this product + * A financial instrument that relates to the definition of this product */ instrument?: InstrumentElement; /** @@ -2510,9 +2507,9 @@ const typeMap: any = { ], "any"), "InstrumentElement": o([ { json: "id", js: "id", typ: r("PurpleInstrumentIdentifiers") }, - { json: "market", js: "market", typ: u(undefined, r("SearchCriteriaMarket")) }, + { json: "market", js: "market", typ: u(undefined, r("OrganizationMarket")) }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("InstrumentType") }, + { json: "type", js: "type", typ: r("PurpleInteractionType") }, ], "any"), "PurpleInstrumentIdentifiers": o([ { json: "BBG", js: "BBG", typ: u(undefined, "") }, @@ -2525,7 +2522,7 @@ const typeMap: any = { { json: "SEDOL", js: "SEDOL", typ: u(undefined, "") }, { json: "ticker", js: "ticker", typ: u(undefined, "") }, ], "any"), - "SearchCriteriaMarket": o([ + "OrganizationMarket": o([ { json: "BBG", js: "BBG", typ: u(undefined, "") }, { json: "COUNTRY_ISOALPHA2", js: "COUNTRY_ISOALPHA2", typ: u(undefined, "") }, { json: "MIC", js: "MIC", typ: u(undefined, "") }, @@ -2556,7 +2553,7 @@ const typeMap: any = { "ContactElement": o([ { json: "id", js: "id", typ: r("PurpleContactIdentifiers") }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("ContactType") }, + { json: "type", js: "type", typ: r("FluffyInteractionType") }, ], "any"), "PurpleContactIdentifiers": o([ { json: "email", js: "email", typ: u(undefined, "") }, @@ -2602,30 +2599,30 @@ const typeMap: any = { { json: "name", js: "name", typ: u(undefined, "") }, ], "any"), "ChatRoomObject": o([ - { json: "id", js: "id", typ: m("any") }, + { json: "id", js: "id", typ: m("") }, { json: "name", js: "name", typ: u(undefined, "") }, { json: "providerName", js: "providerName", typ: "" }, { json: "type", js: "type", typ: r("ChatRoomType") }, { json: "url", js: "url", typ: u(undefined, "") }, ], "any"), "ChatRoom": o([ - { json: "id", js: "id", typ: m("any") }, + { json: "id", js: "id", typ: m("") }, { json: "name", js: "name", typ: u(undefined, "") }, { json: "providerName", js: "providerName", typ: "" }, { json: "type", js: "type", typ: r("ChatRoomType") }, { json: "url", js: "url", typ: u(undefined, "") }, ], "any"), "ChatSearchCriteria": o([ - { json: "criteria", js: "criteria", typ: a(r("SearchCriteria")) }, + { json: "criteria", js: "criteria", typ: a(u(r("OrganizationObject"), "")) }, { json: "type", js: "type", typ: r("ChatSearchCriteriaType") }, { json: "id", js: "id", typ: u(undefined, m("any")) }, { json: "name", js: "name", typ: u(undefined, "") }, ], "any"), - "SearchCriteria": o([ + "OrganizationObject": o([ { json: "id", js: "id", typ: r("Identifiers") }, - { json: "market", js: "market", typ: u(undefined, r("SearchCriteriaMarket")) }, + { json: "market", js: "market", typ: u(undefined, r("OrganizationMarket")) }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("SearchCriteriaType") }, + { json: "type", js: "type", typ: r("TentacledInteractionType") }, ], "any"), "Identifiers": o([ { json: "BBG", js: "BBG", typ: u(undefined, "") }, @@ -2637,13 +2634,13 @@ const typeMap: any = { { json: "RIC", js: "RIC", typ: u(undefined, "") }, { json: "SEDOL", js: "SEDOL", typ: u(undefined, "") }, { json: "ticker", js: "ticker", typ: u(undefined, "") }, - { json: "email", js: "email", typ: u(undefined, "") }, { json: "LEI", js: "LEI", typ: u(undefined, "") }, + { json: "email", js: "email", typ: u(undefined, "") }, ], "any"), "Contact": o([ { json: "id", js: "id", typ: r("FluffyContactIdentifiers") }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("ContactType") }, + { json: "type", js: "type", typ: r("FluffyInteractionType") }, ], "any"), "FluffyContactIdentifiers": o([ { json: "email", js: "email", typ: u(undefined, "") }, @@ -2701,7 +2698,7 @@ const typeMap: any = { { json: "id", js: "id", typ: r("FluffyInstrumentIdentifiers") }, { json: "market", js: "market", typ: u(undefined, r("PurpleMarket")) }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("InstrumentType") }, + { json: "type", js: "type", typ: r("PurpleInteractionType") }, ], "any"), "FluffyInstrumentIdentifiers": o([ { json: "BBG", js: "BBG", typ: u(undefined, "") }, @@ -2805,7 +2802,7 @@ const typeMap: any = { "Organization": o([ { json: "id", js: "id", typ: r("OrganizationIdentifiers") }, { json: "name", js: "name", typ: u(undefined, "") }, - { json: "type", js: "type", typ: r("OrganizationType") }, + { json: "type", js: "type", typ: r("StickyInteractionType") }, ], "any"), "OrganizationIdentifiers": o([ { json: "FDS_ID", js: "FDS_ID", typ: u(undefined, "") }, @@ -2884,7 +2881,7 @@ const typeMap: any = { "ActionType": [ "fdc3.action", ], - "InstrumentType": [ + "PurpleInteractionType": [ "fdc3.instrument", ], "TimeRangeType": [ @@ -2905,7 +2902,7 @@ const typeMap: any = { "ChartType": [ "fdc3.chart", ], - "ContactType": [ + "FluffyInteractionType": [ "fdc3.contact", ], "ContactListType": [ @@ -2927,7 +2924,7 @@ const typeMap: any = { "ChatMessageType": [ "fdc3.chat.message", ], - "SearchCriteriaType": [ + "TentacledInteractionType": [ "fdc3.contact", "fdc3.instrument", "fdc3.organization", @@ -2966,7 +2963,7 @@ const typeMap: any = { "OrderListType": [ "fdc3.orderList", ], - "OrganizationType": [ + "StickyInteractionType": [ "fdc3.organization", ], "PositionType": [ diff --git a/src/index.ts b/src/index.ts index 5b50b6abe..ba462f4bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,6 +42,6 @@ export { BrowserTypes }; declare global { interface Window { - fdc3: DesktopAgent; + fdc3?: DesktopAgent; } } From d6c5dae6dc3556bebb36d1271fb11fedc64766cc Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:46:16 +0100 Subject: [PATCH 149/152] fix failing lint check --- src/api/DesktopAgent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api/DesktopAgent.ts b/src/api/DesktopAgent.ts index 8d9edbdd9..3691ae3f6 100644 --- a/src/api/DesktopAgent.ts +++ b/src/api/DesktopAgent.ts @@ -13,7 +13,6 @@ import { ImplementationMetadata } from './ImplementationMetadata'; import { PrivateChannel } from './PrivateChannel'; import { AppIdentifier } from './AppIdentifier'; import { AppMetadata } from './AppMetadata'; -import { DesktopAgentDetails } from './GetAgent'; import { Intent } from '../intents/Intents'; import { ContextType } from '../context/ContextType'; import { EventHandler, FDC3EventTypes } from './Events'; From 2f2e5ad520c992ee812d71a3ba0a4a7226ae749b Mon Sep 17 00:00:00 2001 From: Roaders <10414642+Roaders@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:22:25 +0100 Subject: [PATCH 150/152] Update CHANGELOG.md Co-authored-by: Kris West --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bdf63115..96428a5d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed -* `window.fdc3` is now an optional property and may or may not be defined. +* `window.fdc3` is now an optional property and may or may not be defined. Applications should use `getAgent()` is now the recommended way of retrieving a reference to the FDC3 API. ([#1386](https://github.com/finos/FDC3/pull/1386)) * `Listener.unsubscribe()` was made async (the return type was changed from `void` to `Promise`) for consistency with the rest of the API. ([#1305](https://github.com/finos/FDC3/pull/1305)) * Added reference materials and supported platforms information for FDC3 in .NET via the [finos/fdc3-dotnet](https://github.com/finos/fdc3-dotnet) project. ([#1108](https://github.com/finos/FDC3/pull/1108)) * The supported platforms page in the FDC3 documentation was moved into the API section as the information it provides all relates to FDC3 Desktop Agent API implementations. ([#1108](https://github.com/finos/FDC3/pull/1108)) From 97ef68d9ea819bb79c83727dd31f006c7019994a Mon Sep 17 00:00:00 2001 From: Giles Roadnight <10414642+Roaders@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:24:18 +0100 Subject: [PATCH 151/152] added GetAgent root export --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index ba462f4bc..26aca3e69 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,7 @@ export * from './api/ContextMetadata'; export * from './api/DesktopAgent'; export * from './api/DisplayMetadata'; export * from './api/Errors'; +export * from './api/GetAgent'; export * from './api/Icon'; export * from './api/Image'; export * from './api/ImplementationMetadata'; From cb4019496b390d9a64f4ed964dfaa1d20681cd6b Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 21 Oct 2024 12:55:14 +0100 Subject: [PATCH 152/152] Update CHANGELOG.md Co-authored-by: Brian Ingenito <28159742+bingenito@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96428a5d5..dd0d86224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed -* `window.fdc3` is now an optional property and may or may not be defined. Applications should use `getAgent()` is now the recommended way of retrieving a reference to the FDC3 API. ([#1386](https://github.com/finos/FDC3/pull/1386)) +* `window.fdc3` is now an optional property and may or may not be defined. Applications should now use `getAgent()` as the recommended way of retrieving a reference to the FDC3 API. ([#1386](https://github.com/finos/FDC3/pull/1386)) * `Listener.unsubscribe()` was made async (the return type was changed from `void` to `Promise`) for consistency with the rest of the API. ([#1305](https://github.com/finos/FDC3/pull/1305)) * Added reference materials and supported platforms information for FDC3 in .NET via the [finos/fdc3-dotnet](https://github.com/finos/fdc3-dotnet) project. ([#1108](https://github.com/finos/FDC3/pull/1108)) * The supported platforms page in the FDC3 documentation was moved into the API section as the information it provides all relates to FDC3 Desktop Agent API implementations. ([#1108](https://github.com/finos/FDC3/pull/1108))