diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3fec90b1f..e8b957c15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,9 +10,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added clarification that `id` field values SHOULD always be strings to context schema definition (a restriction that can't easily be represented in the generated types). ([#1149](https://github.com/finos/FDC3/pull/1149))
* Added requirement that Standard versions SHOULD avoid the use unions in context and API definitions wherever possible as these can be hard to replicate and MUST avoid unions of primitive types as these can be impossible to replicate in other languages. ([#120](https://github.com/finos/FDC3/pull/1200))
+* 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))
### Changed
+* 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))
+
### Deprecated
* Made `IntentMetadata.displayName` optional as it is deprecated. ([#1280](https://github.com/finos/FDC3/pull/1280))
diff --git a/docs/api/ref/Channel.md b/docs/api/ref/Channel.md
index 0bd65aefb..4f949f09d 100644
--- a/docs/api/ref/Channel.md
+++ b/docs/api/ref/Channel.md
@@ -4,6 +4,10 @@ sidebar_label: Channel
title: Channel
hide_title: true
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
# `Channel`
Represents a context channel that applications can join to share context data and provides functions for interacting with it.
@@ -18,6 +22,9 @@ There are differences in behavior when you interact with a User channel via the
Channels each have a unique identifier, some display metadata and operations for broadcasting context to other applications, or receiving context from other applications.
+
+
+
```ts
interface Channel {
// properties
@@ -38,6 +45,24 @@ interface Channel {
}
```
+
+
+
+```csharp
+interface IChannel: IIntentResult
+{
+ string Id { get; }
+ ChannelType Type { get; }
+ IDisplayMetadata? DisplayMetadata { get; }
+ Task Broadcast(IContext context);
+ Task GetCurrentContext(string? contextType);
+ Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext;
+}
+```
+
+
+
+
**See also:**
- [`Context`](Types#context)
@@ -50,26 +75,72 @@ interface Channel {
### `id`
+
+
+
```ts
public readonly id: string;
```
+
+
+
+```csharp
+string Id { get; }
+```
+
+
+
+
Uniquely identifies the channel. It is either assigned by the desktop agent (User Channel) or defined by an application (App Channel).
### `type`
+
+
+
```ts
public readonly type: "user" | "app" | "private";
```
+
+
+
+```csharp
+ChannelType Type { get; }
+
+public enum ChannelType
+{
+ User = 1,
+ App = 2,
+ Private = 3
+}
+```
+
+
+
+
Can be _user_, _app_ or _private_.
### `displayMetadata`
+
+
+
```ts
public readonly displayMetadata?: DisplayMetadata;
```
+
+
+
+```csharp
+IDisplayMetadata? DisplayMetadata { get; }
+```
+
+
+
+
DisplayMetadata can be used to provide display hints for User Channels intended to be visualized and selectable by end users.
**See also:**
@@ -80,10 +151,23 @@ DisplayMetadata can be used to provide display hints for User Channels intended
### `addContextListener`
+
+
+
```ts
public addContextListener(contextType: string | null, handler: ContextHandler): Promise;
```
+
+
+
+```csharp
+Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext;
+```
+
+
+
+
Adds a listener for incoming contexts of the specified _context type_ whenever a broadcast happens on this channel.
If, when this function is called, the channel already contains context that would be passed to the listener it is NOT called or passed this context automatically (this behavior differs from that of the [`fdc3.addContextListener`](DesktopAgent#addcontextlistener) function). Apps wishing to access to the current context of the channel should instead call the [`getCurrentContext(contextType)`](#getcurrentcontext) function.
@@ -94,6 +178,9 @@ Optional metadata about each context message received, including the app that or
Add a listener for any context that is broadcast on the channel:
+
+
+
```ts
const listener = await channel.addContextListener(null, context => {
if (context.type === 'fdc3.contact') {
@@ -107,8 +194,33 @@ const listener = await channel.addContextListener(null, context => {
listener.unsubscribe();
```
+
+
+
+```csharp
+IChannel channel;
+var listener = await channel.AddContextListener(null, (context, metadata) => {
+ if (context.Type == ContextTypes.Contact)
+ {
+ // handle the contact
+ }
+ else if (context.Type == ContextTypes.Instrument) {
+ // handle the instrument
+ }
+});
+
+// later
+listener.Unsubscribe();
+```
+
+
+
+
Adding listeners for specific types of context that is broadcast on the channel:
+
+
+
```ts
const contactListener = await channel.addContextListener('fdc3.contact', contact => {
// handle the contact
@@ -123,6 +235,26 @@ contactListener.unsubscribe();
instrumentListener.unsubscribe();
```
+
+
+
+```csharp
+var contactListener = await channel.AddContextListener("fdc3.contact", (contact, metadata) => {
+ // handle the contact
+});
+
+var instrumentListener = await channel.AddContextListener("fdc3.instrument", (instrument, metadata) => {
+ // handle the instrument
+});
+
+// later
+contactListener.unsubscribe();
+instrumentListener.unsubscribe();
+```
+
+
+
+
**See also:**
- [`Listener`](Types#listener)
@@ -132,10 +264,23 @@ instrumentListener.unsubscribe();
### `broadcast`
-```typescript
+
+
+
+```ts
public broadcast(context: Context): Promise;
```
+
+
+
+```csharp
+Task Broadcast(IContext context);
+```
+
+
+
+
Broadcasts a context on the channel. This function can be used without first joining the channel, allowing applications to broadcast on both App Channels and User Channels that they aren't a member of.
If the broadcast is denied by the channel or the channel is not available, the promise will be rejected with an `Error` with a `message` string from the [`ChannelError`](Errors#channelerror) enumeration.
@@ -148,7 +293,10 @@ If an application attempts to broadcast an invalid context argument the Promise
**Example:**
-```javascript
+
+
+
+```ts
const instrument = {
type: 'fdc3.instrument',
id: {
@@ -163,6 +311,25 @@ try {
}
```
+
+
+
+```csharp
+var instrument = new Instrument(new InstrumentID() { Ticker = "AAPL" });
+
+try
+{
+ channel.Broadcast(instrument);
+}
+catch (Exception ex)
+{
+ // handle error
+}
+```
+
+
+
+
**See also:**
- [`ChannelError`](Errors#channelerror)
@@ -171,10 +338,23 @@ try {
### `getCurrentContext`
+
+
+
```ts
public getCurrentContext(contextType?: string): Promise;
```
+
+
+
+```csharp
+Task GetCurrentContext(string? contextType);
+```
+
+
+
+
When a _context type_ is provided, the most recent context matching the type will be returned, or `null` if no matching context is found.
If no _context type_ is provided, the most recent context that was broadcast on the channel - regardless of type - will be returned. If no context has been set on the channel, it will return `null`.
@@ -187,6 +367,9 @@ If getting the current context fails, the promise will be rejected with an `Erro
Without specifying a context type:
+
+
+
```ts
try {
const context = await channel.getCurrentContext();
@@ -195,8 +378,28 @@ try {
}
```
+
+
+
+```csharp
+try
+{
+ var context = await channel.GetCurrentContext();
+}
+catch (Exception ex)
+{
+ // handle error
+}
+```
+
+
+
+
Specifying a context type:
+
+
+
```ts
try {
const contact = await channel.getCurrentContext('fdc3.contact');
@@ -205,6 +408,23 @@ try {
}
```
+
+
+
+```csharp
+try
+{
+ var context = await channel.GetCurrentContext("fdc3.contact");
+}
+catch (Exception ex)
+{
+ // handle error
+}
+```
+
+
+
+
**See also:**
- [`ChannelError`](Errors#channelerror)
@@ -215,6 +435,9 @@ try {
### `addContextListener` (deprecated)
+
+
+
```ts
/**
* @deprecated Use `addContextListener(null, handler)` instead of `addContextListener(handler)`
@@ -222,6 +445,17 @@ try {
public addContextListener(handler: ContextHandler): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
+
Adds a listener for incoming contexts whenever a broadcast happens on the channel.
**See also:**
diff --git a/docs/api/ref/DesktopAgent.md b/docs/api/ref/DesktopAgent.md
index 204c12d3e..9c452b1f2 100644
--- a/docs/api/ref/DesktopAgent.md
+++ b/docs/api/ref/DesktopAgent.md
@@ -4,13 +4,20 @@ sidebar_label: DesktopAgent
title: DesktopAgent
hide_title: true
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
# `DesktopAgent`
An FDC3 Desktop Agent is a desktop component (or aggregate of components) that serves as an orchestrator for applications in its domain.
A Desktop Agent can be connected to one or more App Directories and will use directories for application identity and discovery. Typically, a Desktop Agent will contain the proprietary logic of a given platform, handling functionality like explicit application interop workflows where security, consistency, and implementation requirements are proprietary.
-It is expected that the `DesktopAgent` interface is made available via the [`window.fdc3`](Globals#windowfdc3-object) global object, and that the [`fdc3Ready`](Globals#fdc3ready-event) event fires when it is ready to be used.
+For details of how implementations of the `DesktopAgent` are made available to applications please see [Supported Platforms](supported-platforms).
+
+
+
```ts
interface DesktopAgent {
@@ -56,14 +63,67 @@ interface DesktopAgent {
}
```
+
+
+
+```csharp
+interface IDesktopAgent
+{
+ // Apps
+ Task Open(IAppIdentifier app, IContext? context = null);
+ Task> FindInstances(IAppIdentifier app);
+ Task GetAppMetadata(IAppIdentifier app);
+
+ // Context
+ Task Broadcast(IContext context);
+ Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext;
+
+ // Intents
+ Task FindIntent(string intent, IContext? context = null, string? resultType = null);
+ Task> FindIntentsByContext(IContext context, string? resultType = null);
+ Task RaiseIntent(string intent, IContext context, IAppIdentifier? app = null);
+ Task RaiseIntentForContext(IContext context, IAppIdentifier? app = null);
+ Task AddIntentListener(string intent, IntentHandler handler) where T : IContext;
+
+ // Channels
+ Task GetOrCreateChannel(string channelId);
+ Task CreatePrivateChannel();
+ Task> GetUserChannels();
+
+ // OPTIONAL channel management functions
+ Task JoinUserChannel(string channelId);
+ Task GetCurrentChannel();
+ Task LeaveCurrentChannel();
+
+ // Implementation Information
+ Task GetInfo();
+}
+```
+
+
+
+
## Functions
### `addContextListener`
+
+
+
```ts
addContextListener(contextType: string | null, handler: ContextHandler): Promise;
```
+
+
+
+```csharp
+Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext;
+```
+
+
+
+
Adds a listener for incoming context broadcasts from the Desktop Agent (via a User channel or [`fdc3.open`](#open) API call). If the consumer is only interested in a context of a particular type, they can specify that type. If the consumer is able to receive context of any type or will inspect types received, then they can pass `null` as the `contextType` parameter to receive all context types.
Context broadcasts are primarily received from apps that are joined to the same User Channel as the listening application, hence, if the application is not currently joined to a User Channel no broadcasts will be received from User channels. If this function is called after the app has already joined a channel and the channel already contains context that matches the type of the context listener, then it will be called immediately and the context passed to the handler function. If `null` was passed as the context type for the listener and the channel contains context, then the handler function will be called immediately with the most recent context - regardless of type.
@@ -74,6 +134,9 @@ Optional metadata about each context message received, including the app that or
**Examples:**
+
+
+
```js
// any context
const listener = await fdc3.addContextListener(null, context => { ... });
@@ -88,6 +151,26 @@ const contactListener = await fdc3.addContextListener('fdc3.contact', (contact,
});
```
+
+
+
+```csharp
+// any context
+var listener = await _desktopAgent.AddContextListener(null, (context, meatadata) => { ... });
+
+// listener for a specific type
+var listener = await _desktopAgent.AddContextListener("fdc3.contact", (contact, metadata) => { ... });
+
+// listener that logs metadata for the message of a specific type
+var contactListener = await _desktopAgent.AddContextListener("fdc3.contact", (contact, metadata) => {
+ System.Diagnostics.Debug.WriteLine($"Received context message\nContext: {contact}\nOriginating app: {metadata?.Source}");
+ // do something else with the context
+});
+```
+
+
+
+
**See also:**
- [`Listener`](Types#listener)
@@ -95,16 +178,33 @@ const contactListener = await fdc3.addContextListener('fdc3.contact', (contact,
- [`ContextHandler`](Types#contexthandler)
### `addEventListener`
+
+
+
+
```ts
addEventListener(type: FDC3EventType | null, handler: EventHandler): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Registers a handler for non-context and non-intent events from the Desktop Agent. If the consumer is only interested in an event of a particular type, they can specify that type. If the consumer is able to receive events of any type or will inspect types received, then they can pass `null` as the `type` parameter to receive all event types.
Whenever the handler function is called it will be passed an event object with details related to the event.
**Examples:**
+
+
+
```js
// any event type
const listener = await fdc3.addEventListener(null, event => { ... });
@@ -114,16 +214,37 @@ const userChannelChangedListener = await fdc3.addEventListener(FDC3EventType.USE
console.log(`Received event ${event.type}\n\tDetails: ${event.details}`);
//do something else with the event
});
-````
+```
+
+
+```
+Not implemented
+```
+
+
+
### `addIntentListener`
+
+
+
```ts
addIntentListener(intent: string, handler: IntentHandler): Promise;
```
+
+
+
+```csharp
+Task AddIntentListener(string intent, IntentHandler handler) where T : IContext;
+```
+
+
+
+
Adds a listener for incoming intents raised by other applications, via calls to [`fdc3.raiseIntent`](#raiseintent) or [`fdc3.raiseIntentForContext`](#raiseintentforcontext). If the application is intended to be launched to resolve raised intents, it SHOULD add its intent listeners as quickly as possible after launch or an error MAY be returned to the caller and the intent and context may not be delivered. The exact timeout used is set by the Desktop Agent implementation, but MUST be at least 15 seconds.
The handler function may return void or a promise that resolves to a [`IntentResult`](Types#intentresult), which is either a [`Context`](Types#context) object, representing any data that should be returned to the app that raised the intent, or a [`Channel`](Channel), a [`PrivateChannel`](PrivateChannel) over which data responses will be sent, or `void`. The `IntentResult` will be returned to the app that raised the intent via the [`IntentResolution`](Metadata#intentresolution) and retrieved from it using the `getResult()` function.
@@ -140,6 +261,9 @@ Optional metadata about each intent & context message received, including the ap
**Examples:**
+
+
+
```js
//Handle a raised intent
const listener = fdc3.addIntentListener('StartChat', context => {
@@ -183,6 +307,26 @@ fdc3.addIntentListener("QuoteStream", async (context) => {
});
```
+
+
+
+```csharp
+//Handle a raised intent
+var listener = await _desktopAgent.AddIntentListener("StartChat", (context, metadata) => {
+ // start chat has been requested by another application
+ // return IIntentResult;
+});
+
+//Handle a raised intent and log the originating app metadata
+var listener = await _desktopAgent.AddIntentListener("StartChat", (contact, metadata) => {
+ System.Diagnostics.Debug.Write($"Received intent StartChat\nContext: {contact}\nOriginating app: {metadata?.Source}");
+ // return IIntentResult;
+});
+```
+
+
+
+
**See also:**
- [Register an Intent Handler](../spec#register-an-intent-handler)
@@ -193,10 +337,23 @@ fdc3.addIntentListener("QuoteStream", async (context) => {
### `broadcast`
+
+
+
```ts
broadcast(context: Context): Promise;
```
+
+
+
+```csharp
+Task Broadcast(IContext context);
+```
+
+
+
+
Publishes context to other apps on the desktop. Calling `broadcast` at the `DesktopAgent` scope will push the context to whatever _User Channel_ the app is joined to. If the app is not currently joined to a channel, calling `fdc3.broadcast` will have no effect. Apps can still directly broadcast and listen to context on any channel via the methods on the `Channel` class.
DesktopAgent implementations SHOULD ensure that context messages broadcast to a channel by an application joined to it are not delivered back to that same application.
@@ -207,6 +364,9 @@ If an application attempts to broadcast an invalid context argument the Promise
**Example:**
+
+
+
```js
const instrument = {
type: 'fdc3.instrument',
@@ -218,16 +378,46 @@ const instrument = {
fdc3.broadcast(instrument);
```
+
+
+
+```csharp
+Instrument instrument = new Instrument(
+ new InstrumentID()
+ {
+ Ticker = "AAPL"
+ }
+);
+
+_desktopAgent.Broadcast(instrument);
+```
+
+
+
+
**See also:**
- [addContextListener](#addcontextlistener)
### `createPrivateChannel`
+
+
+
```ts
createPrivateChannel(): Promise;
```
+
+
+
+```csharp
+Task CreatePrivateChannel();
+```
+
+
+
+
Returns a `Channel` with an auto-generated identity that is intended for private communication between applications. Primarily used to create channels that will be returned to other applications via an IntentResolution for a raised intent.
If the `PrivateChannel` cannot be created, the returned promise MUST be rejected with an `Error` object with a `message` chosen from the [`ChannelError`](Errors#channelerror) enumeration.
@@ -242,6 +432,9 @@ It is intended that Desktop Agent implementations:
**Example:**
+
+
+
```js
fdc3.addIntentListener("QuoteStream", async (context) => {
const channel = await fdc3.createPrivateChannel();
@@ -269,6 +462,39 @@ fdc3.addIntentListener("QuoteStream", async (context) => {
});
```
+
+
+
+```csharp
+_desktopAgent.AddIntentListener("QuoteStream", async (context, metadata) => {
+ var channel = await _desktopAgent.CreatePrivateChannel();
+ var symbol = context?.ID?.Ticker;
+
+ // This gets called when the remote side adds a context listener
+ var addContextListener = channel.OnAddContextListener((contextType) => {
+ // broadcast price quotes as they come in from our quote feed
+ _feed.OnQuote(symbol, (price) => {
+ channel.Broadcast(new Price(price));
+ });
+ });
+
+ // This gets called when the remote side calls Listener.unsubscribe()
+ var unsubscribeListener = channel.OnUnsubscribe((contextType) => {
+ _feed.Stop(symbol);
+ });
+
+ // This gets called if the remote side closes
+ var disconnectListener = channel.OnDisconnect(() => {
+ _feed.stop(symbol);
+ });
+
+ return channel;
+});
+```
+
+
+
+
**See also:**
- [`PrivateChannel`](PrivateChannel)
@@ -277,10 +503,23 @@ fdc3.addIntentListener("QuoteStream", async (context) => {
### `findInstances`
+
+
+
```ts
findInstances(app: AppIdentifier): Promise>;
```
+
+
+
+```csharp
+Task> FindInstances(IAppIdentifier app);
+```
+
+
+
+
Find all the available instances for a particular application.
If the application is not known to the agent, the returned promise should be rejected with the `ResolverError.NoAppsFound` error message. However, if the application is known but there are no instances of the specified app the returned promise should resolve to an empty array.
@@ -289,6 +528,9 @@ If the request fails for another reason, the promise MUST be rejected with an `E
**Example:**
+
+
+
```js
// Retrieve a list of instances of an application
let instances = await fdc3.findInstances({appId: "MyAppId"});
@@ -297,12 +539,39 @@ let instances = await fdc3.findInstances({appId: "MyAppId"});
let resolution = fdc3.raiseIntent("ViewInstrument", context, instances[0]);
```
+
+
+
+```csharp
+// Retrieve a list of instances of an application
+var instances = await _desktopAgent.FindInstances(new AppIdentifier("MyAppId"));
+
+// Target a raised intent at a specific instance
+var resolution = await _desktopAgent.RaiseIntent("ViewInstrument", context, instances.First());
+```
+
+
+
+
### `findIntent`
+
+
+
```ts
findIntent(intent: string, context?: Context, resultType?: string): Promise;
```
+
+
+
+```csharp
+Task FindIntent(string intent, IContext? context = null, string? resultType = null);
+```
+
+
+
+
Find out more information about a particular intent by passing its name, and optionally its context and/or a desired result context type.
`findIntent` is effectively granting programmatic access to the Desktop Agent's resolver.
@@ -316,6 +585,9 @@ Result types may be a type name, the string `"channel"` (which indicates that th
I know 'StartChat' exists as a concept, and want to know which apps can resolve it:
+
+
+
```js
const appIntent = await fdc3.findIntent("StartChat");
// returns a single AppIntent:
@@ -345,8 +617,24 @@ const appIntent = await fdc3.findIntent("StartChat");
// ]
```
+
+
+
+```csharp
+var appIntent = await _desktopAgent.FindIntent("StartChat");
+
+// raise the intent against a particular app
+await _desktopAgent.RaiseIntent(appIntent.Intent.Name, context, appIntent.Apps.First());
+```
+
+
+
+
An optional input context object and/or `resultType` argument may be specified, which the resolver MUST use to filter the returned applications such that each supports the specified input and result types.
+
+
+
```js
const appIntent = await fdc3.findIntent("StartChat", contact);
@@ -371,16 +659,58 @@ const appIntent = await fdc3.findIntent("QuoteStream", instrument, "channel
+
+
+```csharp
+var appIntent = await _desktopAgent.FindIntent("StartChat", contact);
+// returns only apps that support the type of the specified input context:
+// {
+// Intent: { Name: "StartChat" },
+// Apps: { Name: "Symphony" }]
+// }
+
+var appIntent = await _desktopAgent.FindIntent("ViewContact", "fdc3.ContactList");
+// returns only apps that return the specified result type:
+// {
+// Intent: { Name: "ViewContact" },
+// Apps: { AppId: "MyCRM", ResultType: "fdc3.ContactList"}]
+// }
+
+var appIntent = await _desktopAgent.fFindIntent("QuoteStream", instrument, "channel");
+// returns only apps that return a channel which will receive the specified input and result types:
+// {
+// Intent: { Name: "QuoteStream" },
+// Apps: { AppId: "MyOMS", ResultType: "channel"}]
+// }
+```
+
+
+
+
**See also:**
- [`ResolveError`](Errors#resolveerror)
### `findIntentsByContext`
+
+
+
```ts
findIntentsByContext(context: Context, resultType?: string): Promise>;
```
+
+
+
+```csharp
+Task> FindIntentsByContext(IContext context, string? resultType = null);
+```
+
+
+
+
Find all the available intents for a particular context, and optionally a desired result context type.
`findIntentsByContext` is effectively granting programmatic access to the Desktop Agent's resolver.
@@ -394,6 +724,9 @@ The optional `resultType` argument may be a type name, the string `"channel"` (w
I have a context object, and I want to know what I can do with it, hence, I look for intents and apps to resolve them...
+
+
+
```js
const appIntents = await fdc3.findIntentsByContext(context);
@@ -419,8 +752,21 @@ const appIntents = await fdc3.findIntentsByContext(context);
// ];
```
+
+
+
+```csharp
+var appIntents = await _desktopAgent.FindIntentsByContext(context);
+```
+
+
+
+
or I look for only intents that are resolved by apps returning a particular result type
+
+
+
```js
const appIntentsForType = await fdc3.findIntentsByContext(context, "fdc3.ContactList");
// returns for example:
@@ -439,6 +785,30 @@ const selectedApp = startChat.apps[2];
await fdc3.raiseIntent(startChat.intent.name, context, selectedApp);
```
+
+
+
+```csharp
+var appIntentsForType = await _desktopAgent.FindIntentsByContext(context, "fdc3.ContactList");
+// returns for example:
+// [{
+// Intent: { Name: "ViewContact" },
+// Apps: [{ AppId: "Symphony" }, { AppId: "MyCRM", ResultType: "fdc3.ContactList"}]
+// }];
+
+// select a particular intent to raise
+var startChat = appIntentsForType.First();
+
+// target a particular app or instance
+var selectedApp = startChat.Apps.First();
+
+// raise the intent, passing the given context, targeting the app
+await _desktopAgent.RaiseIntent(startChat.Intent.Name, context, selectedApp);
+```
+
+
+
+
**See also:**
- [`findIntent()`](#findintent)
@@ -446,22 +816,48 @@ await fdc3.raiseIntent(startChat.intent.name, context, selectedApp);
### `getAppMetadata`
+
+
+
```ts
getAppMetadata(app: AppIdentifier): Promise;
```
+
+
+
+```csharp
+Task GetAppMetadata(IAppIdentifier app);
+```
+
+
+
+
Retrieves the [`AppMetadata`](Metadata#appmetadata) for an [`AppIdentifier`](Types#appidentifier), which provides additional metadata (such as icons, a title and description) from the App Directory record for the application, that may be used for display purposes.
If the app is not found, the promise MUST be rejected with an `Error` Object with the `message` given by [`ResolveError.TargetAppUnavailable`](Errors#resolveerror), or (if connected to a Desktop Agent Bridge) an error from the [`BridgingError`](Errors#bridgingerror) enumeration.
+**Example:**
-**Examples:**
+
+
```js
let appIdentifier = { appId: "MyAppId@my.appd.com" }
let appMetadata = await fdc3.getAppMetadata(appIdentifier);
```
+
+
+
+```csharp
+var appIdentifier = new AppIdentifier("MyAppId@my.appd.com");
+var appMetadata = await _desktopAgent.GetAppMetadata(appIdentifier);
+```
+
+
+
+
**See also:**
- [`AppMetadata`](Metadata#appmetadata)
@@ -469,37 +865,80 @@ let appMetadata = await fdc3.getAppMetadata(appIdentifier);
### `getCurrentChannel`
+
+
+
```ts
getCurrentChannel() : Promise;
```
+
+
+
+```csharp
+Task GetCurrentChannel();
+```
+
+
+
+
OPTIONAL function that returns the `Channel` object for the current User channel membership. In most cases, an application's membership of channels SHOULD be managed via UX provided to the application by the desktop agent, rather than calling this function directly.
Returns `null` if the app is not joined to a channel.
-**Examples:**
+**Example:**
+
+
+
```js
// get the current channel membership
let current = await fdc3.getCurrentChannel();
```
+
+
+
+```csharp
+// get the current channel membership
+var current = await _desktopAgent.GetCurrentChannel();
+```
+
+
+
+
**See also:**
- [`Channel`](Channel)
### `getInfo`
+
+
+
```ts
getInfo(): Promise;
```
+
+
+
+```csharp
+Task GetInfo();
+```
+
+
+
+
Retrieves information about the FDC3 Desktop Agent implementation, including the supported version of the FDC3 specification, the name of the provider of the implementation, its own version number, details of whether optional API features are implemented and the metadata of the calling application according to the desktop agent.
Returns an [`ImplementationMetadata`](Metadata#implementationmetadata) object. This metadata object can be used to vary the behavior of an application based on the version supported by the Desktop Agent and for logging purposes.
**Example:**
+
+
+
```js
import {compareVersionNumbers, versionIsAtLeast} from '@finos/fdc3';
@@ -510,13 +949,38 @@ if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), "1.2")) {
}
```
+
+
+
+```csharp
+var version = (await _desktopAgent.GetInfo()).Fdc3Version;
+```
+
+
+
+
The `ImplementationMetadata` object returned also includes the metadata for the calling application, according to the Desktop Agent. This allows the application to retrieve its own `appId`, `instanceId` and other details, e.g.:
+
+
+
```js
let implementationMetadata = await fdc3.getInfo();
let {appId, instanceId} = implementationMetadata.appMetadata;
```
+
+
+
+```csharp
+var implementationMetadata = await _desktopAgent.GetInfo();
+var appId = implementationMetadata.AppMetadata.AppId;
+var instanceId = implementationMetadata.AppMetadata.InstanceId;
+```
+
+
+
+
**See also:**
- [`ImplementationMetadata`](Metadata#implementationmetadata)
@@ -524,55 +988,129 @@ let {appId, instanceId} = implementationMetadata.appMetadata;
### `getOrCreateChannel`
+
+
+
```ts
getOrCreateChannel(channelId: string): Promise;
```
+
+
+
+```csharp
+Task GetOrCreateChannel(string channelId);
+```
+
+
+
+
Returns a `Channel` object for the specified channel, creating it (as an _App_ channel) if it does not exist.
If the Channel cannot be created or access was denied, the returned promise MUST be rejected with an `Error` Object with a `message` chosen from the `ChannelError` enumeration.
**Example:**
-```js
+
+
+
+```ts
try {
const myChannel = await fdc3.getOrCreateChannel("myChannel");
myChannel.addContextListener(null, context => { /* do something with context */});
}
-catch (err){
+catch (err: ChannelError) {
//app could not register the channel
}
```
+
+
+
+```csharp
+try
+{
+ var myChannel = await _desktopAgent.GetOrCreateChannel("myChannel");
+ await myChannel.AddContextListener(null, (context, metadata) => { /* do something with context */});
+}
+catch (Exception ex)
+{
+ //app could not register the channel
+}
+```
+
+
+
+
**See also:**
- [`Channel`](Channel)
### `getUserChannels`
+
+
+
```ts
getUserChannels() : Promise>;
```
+
+
+
+```csharp
+Task> GetUserChannels();
+```
+
+
+
+
Retrieves a list of the User Channels available for the app to join.
**Example:**
+
+
+
```js
const userChannels = await fdc3.getUserChannels();
const redChannel = userChannels.find(c => c.id === 'red');
```
+
+
+
+```csharp
+var userChannels = await _desktopAgent.GetUserChannels();
+var redChannel = userChannels.First(c => c.Id == "red");
+```
+
+
+
+
**See also:**
- [`Channel`](Channel)
### `joinUserChannel`
+
+
+
```ts
joinUserChannel(channelId: string) : Promise;
```
+
+
+
+```csharp
+Task JoinUserChannel(string channelId);
+```
+
+
+
+
OPTIONAL function that joins the app to the specified User channel. In most cases, applications SHOULD be joined to channels via UX provided to the application by the desktop agent, rather than calling this function directly.
If an app is joined to a channel, all `fdc3.broadcast` calls will go to the channel, and all listeners assigned via `fdc3.addContextListener` will listen on the channel.
@@ -583,7 +1121,10 @@ An app can only be joined to one channel at a time.
If an error occurs (such as the channel is unavailable or the join request is denied) the promise MUST be rejected with an `Error` Object with a `message` chosen from the [`ChannelError`](Errors#channelerror) enumeration.
-**Examples:**
+**Example:**
+
+
+
```js
// get all user channels
@@ -593,24 +1134,55 @@ const channels = await fdc3.getUserChannels();
// join the channel on selection
fdc3.joinUserChannel(selectedChannel.id);
+```
+
+
+
+
+```csharp
+// get all user channels
+var channels = await _desktopAgent.GetUserChannels();
+
+// create UI to pick from the User channels
+// join the channel on selection
+_desktopAgent.JoinUserChannel(selectedChannel.Id);
```
+
+
+
**See also:**
- [`getUserChannels`](#getuserchannels)
### `leaveCurrentChannel`
+
+
+
```ts
leaveCurrentChannel() : Promise;
```
+
+
+
+```csharp
+Task LeaveCurrentChannel();
+```
+
+
+
+
OPTIONAL function that removes the app from any User channel membership. In most cases, an application's membership of channels SHOULD be managed via UX provided to the application by the desktop agent, rather than calling this function directly.
Context broadcast and listening through the top-level `fdc3.broadcast` and `fdc3.addContextListener` will be a no-op when the app is not joined to a User channel.
-**Examples:**
+**Example:**
+
+
+
```js
//desktop-agent scope context listener
@@ -624,12 +1196,42 @@ redChannel.addContextListener(null, channelListener);
```
+
+
+
+```csharp
+//desktop-agent scope context listener
+var fdc3Listener = await _desktopAgent.AddContextListener(null, (context, metadata) => { });
+
+await _desktopAgent.LeaveCurrentChannel();
+//the fdc3Listener will now cease receiving context
+
+//listening on a specific channel though, will continue to work
+redChannel.AddContextListener(null, channelListener);
+```
+
+
+
+
### `open`
+
+
+
```ts
open(app: AppIdentifier, context?: Context): Promise;
```
+
+
+
+```csharp
+Task Open(IAppIdentifier app, IContext? context = null);
+```
+
+
+
+
Launches an app, specified via an [`AppIdentifier`](Types#appidentifier) object.
The `open` method differs in use from [`raiseIntent`](#raiseintent). Generally, it should be used when the target application is known but there is no specific intent. For example, if an application is querying an App Directory, `open` would be used to open an app returned in the search results.
@@ -644,6 +1246,9 @@ If an error occurs while opening the app, the promise MUST be rejected with an `
**Example:**
+
+
+
```js
// Open an app without context, using an AppIdentifier object to specify the target
let appIdentifier = { appId: 'myApp-v1.0.1' };
@@ -653,6 +1258,21 @@ let instanceIdentifier = await fdc3.open(appIdentifier);
let instanceIdentifier = await fdc3.open(appIdentifier, context);
```
+
+
+
+```csharp
+// Open an app without context, using an AppIdentifier object to specify the target
+var appIdentifier = new AppIdentifier("myApp-v1.0.1");
+var instanceIdentifier = await _desktopAgent.Open(appIdentifier);
+
+// Open an app with context
+var instanceIdentifier = await _desktopAgent.Open(appIdentifier, context);
+```
+
+
+
+
**See also:**
- [`Context`](Types#context)
@@ -662,10 +1282,23 @@ let instanceIdentifier = await fdc3.open(appIdentifier, context);
### `raiseIntent`
+
+
+
```ts
raiseIntent(intent: string, context: Context, app?: AppIdentifier): Promise;
```
+
+
+
+```csharp
+Task RaiseIntent(string intent, IContext context, IAppIdentifier? app = null);
+```
+
+
+
+
Raises a specific intent for resolution against apps registered with the desktop agent.
The desktop agent MUST resolve the correct app to target based on the provided intent name and context data. If multiple matching apps are found, a method for resolving the intent to a target app, such as presenting the user with a resolver UI allowing them to pick an app, SHOULD be provided.
@@ -681,7 +1314,10 @@ Issuing apps may optionally wait on the promise that is returned by the `getResu
**Example:**
-```js
+
+
+
+```ts
// raise an intent for resolution by the desktop agent
// a resolver UI may be displayed, or another method of resolving the intent to a
// target applied, if more than one application can resolve the intent
@@ -707,11 +1343,37 @@ try {
} else {
console.error(`${resolution.source} didn't return anything`
}
-} catch(error) {
+}
+catch (error: ResultError) {
console.error(`${resolution.source} returned a result error: ${error}`);
}
```
+
+
+
+```csharp
+// raise an intent for resolution by the desktop agent
+// a resolver UI may be displayed, or another method of resolving the intent to a
+// target applied, if more than one application can resolve the intent
+await _desktopAgent.RaiseIntent("StartChat", context);
+
+// or find apps to resolve an intent to start a chat with a given contact
+var appIntent = await _desktopAgent.FindIntent("StartChat", context);
+
+// use the metadata of an app or app instance to describe the target app for the intent
+await _desktopAgent.RaiseIntent("StartChat", context, appIntent.Apps.First());
+
+//Raise an intent without a context by using the null context type
+await _desktopAgent.RaiseIntent("StartChat", ContextType.Nothing);
+
+//Raise an intent and retrieve a result from the IntentResolution
+IIntentResolution resolution = await _desktopAgent.RaiseIntent("intentName", context);
+```
+
+
+
+
**See also:**
- [Raising Intents](../spec#raising-intents)
@@ -724,10 +1386,23 @@ try {
### `raiseIntentForContext`
+
+
+
```ts
raiseIntentForContext(context: Context, app?: AppIdentifier): Promise;
```
+
+
+
+```csharp
+Task RaiseIntentForContext(IContext context, IAppIdentifier? app = null);
+```
+
+
+
+
Finds and raises an intent against apps registered with the desktop agent based purely on the type of the context data.
The desktop agent SHOULD first resolve to a specific intent based on the provided context if more than one intent is available for the specified context. This MAY be achieved by displaying a resolver UI. It SHOULD then resolve to a specific app to handle the selected intent and specified context.
@@ -741,6 +1416,9 @@ If a target intent and app cannot be found with the criteria provided or the use
**Example:**
+
+
+
```js
// Display a resolver UI for the user to select an intent and application to resolve it
const intentResolution = await fdc3.raiseIntentForContext(context);
@@ -749,6 +1427,20 @@ const intentResolution = await fdc3.raiseIntentForContext(context);
await fdc3.raiseIntentForContext(context, targetAppIdentifier);
```
+
+
+
+```csharp
+// Display a resolver UI for the user to select an intent and application to resolve it
+var intentResolution = await _desktopAgent.RaiseIntentForContext(context);
+
+// Resolve against all intents registered by a specific target app for the specified context
+await _desktopAgent.RaiseIntentForContext(context, targetAppIdentifier);
+```
+
+
+
+
**See also:**
- [Raising Intents](../spec#raising-intents)
@@ -762,10 +1454,23 @@ await fdc3.raiseIntentForContext(context, targetAppIdentifier);
### `addContextListener` (deprecated)
+
+
+
```ts
addContextListener(handler: ContextHandler): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Adds a listener for incoming context broadcasts from the Desktop Agent. Provided for backwards compatibility with versions FDC3 standard <2.0.
**See also:**
@@ -774,10 +1479,23 @@ Adds a listener for incoming context broadcasts from the Desktop Agent. Provided
### `getSystemChannels` (deprecated)
+
+
+
```ts
getSystemChannels() : Promise>;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Alias to the [`getUserChannels`](#getuserchannels) function provided for backwards compatibility with version 1.1 & 1.2 of the FDC3 standard.
**See also:**
@@ -785,10 +1503,23 @@ Alias to the [`getUserChannels`](#getuserchannels) function provided for backwar
### `joinChannel` (deprecated)
+
+
+
```ts
joinChannel(channelId: string) : Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Alias to the [`joinUserChannel`](#joinuserchannel) function provided for backwards compatibility with version 1.1 & 1.2 of the FDC3 standard.
**See also:**
@@ -797,10 +1528,23 @@ Alias to the [`joinUserChannel`](#joinuserchannel) function provided for backwar
### `open` (deprecated)
+
+
+
```ts
open(name: string, context?: Context): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Version of `open` that launches an app by name rather than `AppIdentifier`. Provided for backwards compatibility with versions of the FDC3 Standard <2.0.
**See also:**
@@ -809,10 +1553,23 @@ Version of `open` that launches an app by name rather than `AppIdentifier`. Prov
### `raiseIntent` (deprecated)
+
+
+
```ts
raiseIntent(intent: string, context: Context, name: string): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Version of `raiseIntent` that targets an app by name rather than `AppIdentifier`. Provided for backwards compatibility with versions of the FDC3 Standard <2.0.
**See also:**
@@ -821,10 +1578,23 @@ Version of `raiseIntent` that targets an app by name rather than `AppIdentifier`
### `raiseIntentForContext` (deprecated)
+
+
+
```ts
raiseIntentForContext(context: Context, name: string): Promise;
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
Version of `raiseIntentForContext` that targets an app by name rather than `AppIdentifier`. Provided for backwards compatibility with versions of the FDC3 Standard <2.0.
**See also:**
diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md
index b5a9144f7..6ce1b39ae 100644
--- a/docs/api/ref/Errors.md
+++ b/docs/api/ref/Errors.md
@@ -2,11 +2,17 @@
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.
## `ChannelError`
-```typescript
+
+
+
+```ts
enum ChannelError {
/** Returned if the specified channel is not found when attempting to join a
* channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).
@@ -32,6 +38,43 @@ enum ChannelError {
}
```
+
+
+
+```csharp
+public static class ChannelError
+{
+ ///
+ /// Returned if the specified channel is not found when attempting to join a
+ /// channel via the `JoinUserChannel` function of the DesktopAgent.
+ ///
+ public static readonly string NoChannelFound = nameof(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 is denied.
+ ///
+ public static readonly string AccessDenied = nameof(AccessDenied);
+
+ ///
+ /// SHOULD be returned when a channel cannot be created or retrieved via the
+ /// `GetOrCreateChannel` method of the DesktopAgent.
+ ///
+ public static readonly string CreationFailed = nameof(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.
+ ///
+ public static readonly string MalformedContext = nameof(MalformedContext);
+}
+```
+
+
+
+
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:**
@@ -45,7 +88,10 @@ Contains constants representing the errors that can be encountered when calling
## `OpenError`
-```typescript
+
+
+
+```ts
enum OpenError {
/** Returned if the specified application is not found.*/
AppNotFound = 'AppNotFound',
@@ -75,6 +121,46 @@ enum OpenError {
}
```
+
+
+
+```csharp
+public static class OpenError
+{
+ ///
+ /// Returned if the specified application is not found.
+ ///
+ public static readonly string AppNotFound = nameof(AppNotFound);
+
+ ///
+ /// Returned if the specified application fails to launch correctly.
+ ///
+ public static readonly string ErrorOnLaunch = nameof(ErrorOnLaunch);
+
+ ///
+ /// Returned if the specified application launches but fails to add a context
+ /// listener in order to receive the context passed to the `Open` call.
+ ///
+ public static readonly string AppTimeout = nameof(AppTimeout);
+
+ ///
+ /// Returned if the FDC3 desktop agent implementation is not currently able
+ /// to handle the request.
+ ///
+ public static readonly string ResolverUnavailable = nameof(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.
+ ///
+ public static readonly string MalformedContext = nameof(MalformedContext);
+}
+```
+
+
+
+
Contains constants representing the errors that can be encountered when calling the [`open`](DesktopAgent#open) method on the [DesktopAgent](DesktopAgent) object.
**See also:**
@@ -83,7 +169,10 @@ Contains constants representing the errors that can be encountered when calling
## `ResolveError`
-```typescript
+
+
+
+```ts
export enum ResolveError {
/** SHOULD be returned if no apps are available that can resolve the intent
* and context combination.
@@ -134,6 +223,68 @@ export enum ResolveError {
}
```
+
+
+
+```csharp
+public static class ResolveError
+{
+ ///
+ /// SHOULD be returned if no apps are available that can resolve the intent
+ /// and context combination.
+ ///
+ public static readonly string NoAppsFound = nameof(NoAppsFound);
+
+ ///
+ /// Returned if the FDC3 desktop agent implementation is not currently able
+ /// to handle the request.
+ ///
+ public static readonly string ResolverUnavailable = nameof(ResolverUnavailable);
+
+ ///
+ /// 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.
+ ///
+ public static readonly string ResolverTimeout = nameof(ResolverTimeout);
+
+ ///
+ /// Returned if the user cancelled the resolution request, for example by
+ /// closing or cancelling a resolver UI.
+ ///
+ public static readonly string UserCancelledResolution = nameof(UserCancelledResolution);
+
+ ///
+ /// Returned if a specified target application is not available or a new
+ /// instance of it cannot be opened.
+ ///
+ public static readonly string TargetAppUnavailable = nameof(TargetAppUnavailable);
+
+ ///
+ /// Returned if a specified target application instance is not available,
+ /// for example because it has been closed.
+ ///
+ public static readonly string TargetInstanceUnavailable = nameof(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.
+ ///
+ public static readonly string IntentDeliveryFailed = nameof(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.
+ ///
+ public static readonly string MalformedContext = nameof(MalformedContext);
+}
+```
+
+
+
+
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:**
@@ -145,7 +296,10 @@ Contains constants representing the errors that can be encountered when calling
## `ResultError`
-```typescript
+
+
+
+```ts
enum ResultError {
/** Returned if the intent handler exited without returning a valid result
* (a promise resolving to a Context, Channel object or void).
@@ -159,6 +313,29 @@ enum ResultError {
}
```
+
+
+
+```csharp
+public static class ResultError
+{
+ ///
+ /// Returned if the intent handler exited without returning a valid result
+ /// (a Task resolving to a Context, Channel object or void).
+ ///
+ public static readonly string NoResultReturned = nameof(NoResultReturned);
+
+ ///
+ /// Returned if the `IntentHandler` function processing the raised intent
+ /// throws an error.
+ ///
+ public static readonly string IntentHandlerRejected = nameof(IntentHandlerRejected);
+}
+```
+
+
+
+
Contains constants representing the errors that can be encountered when calling the [`getResult`](DesktopAgent#findintent) method on the [IntentResolution](Metadata#intentresolution) Object.
**See also:**
@@ -171,7 +348,10 @@ Contains constants representing the errors that can be encountered when calling
`@experimental`
-```typescript
+
+
+
+```ts
enum BridgingError {
/** @experimental Returned if a Desktop Agent did not return a response, via
* Desktop Agent Bridging, within the alloted timeout. */
@@ -192,6 +372,17 @@ enum BridgingError {
}
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
+
Contains constants representing the errors that can be encountered when queries are forwarded to a Desktop Agent Bridge, but one or more remote Desktop Agents connected to it disconnects, times-out or a malformed message is encountered while a particular request is in flight. These errors may be returned via the FDC3 API when a Desktop Agent is (or was) connected to a Desktop Agent Bridge.
**See also:**
diff --git a/docs/api/ref/Globals.md b/docs/api/ref/Globals.md
deleted file mode 100644
index 4e419c9af..000000000
--- a/docs/api/ref/Globals.md
+++ /dev/null
@@ -1,64 +0,0 @@
----
-title: Globals
----
-
-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.
-
-## `window.fdc3` Object
-
-The global `fdc3` object can be used to access FDC3 API operations, if the current desktop agent supports FDC3.
-
-**Example:**
-
-```ts
-// check if fdc3 is available
-if (window.fdc3) {
- // raise an intent
- await window.fdc3.raiseIntent('StartChat', {
- type: 'fdc3.contact',
- id: { email: 'johndoe@mail.com' }
- })
-}
-```
-
-## `fdc3Ready` Event
-
-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.
-
-**Example:**
-
-```ts
-function fdc3Action() {
- // Make some fdc3 API calls here
-}
-
-if (window.fdc3) {
- fdc3Action();
-} else {
- window.addEventListener('fdc3Ready', fdc3Action);
-}
-```
-
-## `fdc3Ready()` Function
-
-If you are using the `@finos/fdc3` NPM package, it includes a handy wrapper function that will check for the existence of `window.fdc3` and wait on the `fdc3Ready` event for you.
-
-It returns a promise that will resolve immediately if the `window.fdc3` global is already defined, or reject with an error if the `fdc3Ready` event doesn't fire after a specified timeout period (default: 5 seconds).
-
-**Example:**
-
-```ts
-import { fdc3Ready, broadcast } from '@finos/fdc3'
-
-async function fdc3Action() {
- try {
- await fdc3Ready(1000); // wait for (at most) 1 second
- broadcast({
- type: 'fdc3.instrument',
- id: { ticker: 'AAPL' }
- })
- } catch (error) {
- // handle error
- }
-}
-```
diff --git a/docs/api/ref/Metadata.md b/docs/api/ref/Metadata.md
index cf4d89ed9..8279d4d4c 100644
--- a/docs/api/ref/Metadata.md
+++ b/docs/api/ref/Metadata.md
@@ -2,10 +2,16 @@
title: Metadata
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
FDC3 API operations return various types of metadata.
## `AppIntent`
+
+
+
```ts
interface AppIntent {
/** Details of the intent whose relationship to resolving applications is
@@ -18,6 +24,28 @@ interface AppIntent {
}
```
+
+
+
+```csharp
+interface IAppIntent
+{
+ ///
+ /// Details of the intent whose relationship to resolving application is
+ /// being described.
+ ///
+ IIntentMetadata Intent { get; }
+
+ ///
+ /// Details of applications that can resolve the intent.
+ ///
+ IEnumerable Apps { get; }
+}
+```
+
+
+
+
An interface that represents the binding of an intent to apps, returned as part of intent discovery.
For each intent, it reference the applications that support that intent.
@@ -30,6 +58,9 @@ For each intent, it reference the applications that support that intent.
## `AppMetadata`
+
+
+
```ts
interface AppMetadata extends AppIdentifier {
/**
@@ -81,6 +112,58 @@ interface AppMetadata extends AppIdentifier {
}
```
+
+
+
+```csharp
+interface IAppMetadata : IAppIdentifier
+{
+ ///
+ /// The unique app name that can be used with the open and raiseIntent calls.
+ ///
+ string? Name { get; }
+
+ ///
+ /// The Version of the application.
+ ///
+ string? Version { get; }
+
+ ///
+ /// A more user-friendly application title that can be used to render UI elements.
+ ///
+ string? Title { get; }
+
+ ///
+ /// A tooltip for the application that can be used to render UI elements.
+ ///
+ string? Tooltip { get; }
+
+ ///
+ /// A longer, multi-paragraph description for the application that could include markup.
+ ///
+ string? Description { get; }
+
+ ///
+ /// A list of icon URLs for the application that can be used to render UI elements.
+ ///
+ IEnumerable Icons { get; }
+
+ ///
+ /// A list of image URLs for the application that can be used to render UI elements.
+ ///
+ IEnumerable Screenshots { get; }
+
+ ///
+ /// The type of output returned for any intent specified during resolution. May express a particular context type,
+ /// channel, or channel with specified type
+ ///
+ string? ResultType { get; }
+}
+```
+
+
+
+
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.
@@ -99,6 +182,9 @@ Note that as `AppMetadata` instances are also `AppIdentifiers` they may be passe
## `ContextMetadata`
+
+
+
```ts
interface ContextMetadata {
/** Identifier for the app instance that sent the context and/or intent.
@@ -108,6 +194,22 @@ interface ContextMetadata {
}
```
+
+
+
+```csharp
+interface IContextMetadata
+{
+ ///
+ /// Identifier for the app instance that sent the context and/or intent.
+ ///
+ IAppIdentifier? Source { get; }
+}
+```
+
+
+
+
Metadata relating to a context or intent & context received through the `addContextListener` and `addIntentListener` functions. Currently identifies the app that originated the context or intent message.
[`@experimental`](../../fdc3-compliance#experimental-features) Introduced in FDC3 2.0 and may be refined by further changes outside the normal FDC3 versioning policy.
@@ -123,6 +225,9 @@ Metadata relating to a context or intent & context received through the `addCont
## `DisplayMetadata`
+
+
+
```ts
interface DisplayMetadata {
/**
@@ -141,6 +246,33 @@ interface DisplayMetadata {
}
```
+
+
+
+```csharp
+interface IDisplayMetadata
+{
+ ///
+ /// A user-readable name for this channel, e.g: Red.
+ ///
+ string? Name { get; }
+
+ ///
+ /// The color that should be associated within this channel when displaying this
+ /// channein in a UI, e.g: '0xFF0000'.
+ ///
+ string? Color { get; }
+
+ ///
+ /// A URL of an image that can be used to display this channel.
+ ///
+ string? Glyph { get; }
+}
+```
+
+
+
+
A desktop agent (typically for _system_ channels) may want to provide additional information about how a channel can be represented in a UI. A common use case is for color linking.
**See also:**
@@ -150,7 +282,10 @@ A desktop agent (typically for _system_ channels) may want to provide additional
## `Icon`
-```typescript
+
+
+
+```ts
interface Icon {
src: string;
size?: string;
@@ -158,13 +293,42 @@ interface Icon {
}
```
+
+
+
+```csharp
+interface IIcon
+{
+ ///
+ /// The icon url
+ ///
+ string Src { get; }
+
+ ///
+ /// The icon dimensions, formatted as '{height}x{width}'
+ ///
+ string? Size { get; }
+
+ ///
+ /// Icon media type. If not present, the Desktop Agent may use the src file extension.
+ ///
+ string? Type { get; }
+}
+```
+
+
+
+
Metadata relating to a single icon image at a remote URL, used to represent an application in a user interface.
AppMetadata includes an icons property allowing multiple icon types to be specified. Various properties may be used by the Desktop Agent to decide which icon is the most suitable to be used considering the application chooser UI, device DPI and formats supported by the system.
**Example:**
-```js
+
+
+
+```ts
"icons": [
{
"src": "https://app.foo.icon/app_icons/lowres.webp",
@@ -179,6 +343,16 @@ AppMetadata includes an icons property allowing multiple icon types to be specif
]
```
+
+
+
+```csharp
+IIcon? icon = appMetadata?.Icons.Where(icon => icon.Size == "48x48").First();
+```
+
+
+
+
**Properties:**
- **`src`:** The fully qualified url to the icon.
@@ -191,7 +365,10 @@ AppMetadata includes an icons property allowing multiple icon types to be specif
## `Image`
-```typescript
+
+
+
+```ts
interface Image {
src: string;
size?: string;
@@ -200,13 +377,48 @@ interface Image {
}
```
+
+
+
+```csharp
+interface IImage
+{
+ ///
+ /// The icon url
+ ///
+ string Src { get; }
+
+ ///
+ /// The icon dimensions, formatted as '{height}x{width}'
+ ///
+ string? Size { get; }
+
+ ///
+ /// Icon media type. If not present, the Desktop Agent may use the src file extension.
+ ///
+ string? Type { get; }
+
+
+ ///
+ /// Caption for the image
+ ///
+ string? Label { get; }
+}
+```
+
+
+
+
Metadata relating to a single image at a remote URL, used to represent screenshot images.
AppMetadata includes a screenshots property allowing multiple images to be specified. Various properties may be used by the Desktop Agent to decide which image(s) are the most suitable to be used considering the application chooser UI, device DPI and formats supported by the system.
**Example:**
-```js
+
+
+
+```ts
"screenshots": [
{
"src": "https://app.foo.icon/app_screenshots/dashboard.png",
@@ -223,6 +435,19 @@ AppMetadata includes a screenshots property allowing multiple images to be speci
]
```
+
+
+
+```csharp
+foreach (IImage image in appMetadata.Screenshots)
+{
+ System.Diagnostics.Debug.WriteLine(image.Src);
+}
+```
+
+
+
+
**Properties:**
- **`src`:** The fully qualified url to the image.
@@ -235,6 +460,9 @@ AppMetadata includes a screenshots property allowing multiple images to be speci
## `ImplementationMetadata`
+
+
+
```ts
interface ImplementationMetadata {
/** The version number of the FDC3 specification that the implementation
@@ -275,6 +503,58 @@ interface ImplementationMetadata {
}
```
+
+
+
+```csharp
+interface IImplementationMetadata
+{
+ ///
+ /// 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.
+ ///
+ string Fdc3Version { get; }
+
+ ///
+ /// The name of the provider of the FDC3 Desktop Agent Implementation (e.g. Finsemble, Glue42, OpenFin etc.).
+ ///
+ string Provider { get; }
+
+ ///
+ /// The version of the provider of the FDC3 Desktop Agent Implementation (e.g. 5.3.0).
+ ///
+ string ProviderVersion { get; }
+
+ ///
+ /// Metadata indicating whether the Desktop Agent implements optional features of the Desktop Agent API.
+ ///
+ OptionalDesktopAgentFeatures OptionalFeatures { get; }
+
+ ///
+ /// The calling application instance's own metadata according to the Desktop Agent
+ ///
+ IAppMetadata AppMetadata { get; }
+}
+
+class OptionalDesktopAgentFeatures
+{
+ ///
+ /// Used to indicate whether the exposure of 'originating app metadata' for context and intent
+ /// messages is supported by the Desktop Agent.
+ ///
+ public bool OriginatingAppMetadata { get; set; }
+
+ ///
+ /// Used to indicate whether the optional 'JoinUserChannel', 'GetCurrentChannel', and 'LeaveCurrentChannel'
+ /// are implemented by the Desktop Agent.
+ ///
+ public bool UserChannelMembershipAPIs { get; set; }
+}
+```
+
+
+
+
Metadata relating to the FDC3 [DesktopAgent](DesktopAgent) object and its provider, including the supported version of the FDC3 specification, the name of the provider of the implementation, its own version number and the metadata of the calling application according to the desktop agent.
**See also:**
@@ -284,6 +564,9 @@ Metadata relating to the FDC3 [DesktopAgent](DesktopAgent) object and its provid
## `IntentMetadata`
+
+
+
```ts
interface IntentMetadata {
/** The unique name of the intent that can be invoked by the raiseIntent call. */
@@ -297,6 +580,27 @@ interface IntentMetadata {
}
```
+
+
+
+```csharp
+interface IIntentMetadata
+{
+ ///
+ /// The unique name of the intent that can be invoked by the raiseIntent call.
+ ///
+ string Name { get; }
+
+ ///
+ /// A friendly display name for the intent that should be used to render UI elements.
+ ///
+ string DisplayName { get; }
+}
+```
+
+
+
+
The interface used to describe an intent within the platform.
**See also:**
@@ -305,6 +609,9 @@ The interface used to describe an intent within the platform.
## `IntentResolution`
+
+
+
```ts
interface IntentResolution {
@@ -337,11 +644,42 @@ interface IntentResolution {
}
```
+
+
+
+```csharp
+interface IIntentResolution
+{
+ ///
+ /// The application that resolved the intent.
+ ///
+ IAppIdentifier Source { get; }
+
+ ///
+ /// The intent that was raised.
+ ///
+ string Intent { get; }
+
+ ///
+ /// The version number of the Intents schema being used.
+ ///
+ string? Version { get; }
+
+ Task GetResult();
+}
+```
+
+
+
+
IntentResolution provides a standard format for data returned upon resolving an intent.
**Examples:**
-```js
+
+
+
+```ts
// Resolve a "Chain" type intent
let resolution = await agent.raiseIntent("intentName", context);
@@ -353,7 +691,7 @@ try {
//Some time later
await agent.raiseIntent("UpdateOrder", context, resolution.source);
}
-catch (err) { ... }
+catch (err: ResolveError) { ... }
//Resolve a "Client-Service" type intent with a data or channel response
let resolution = await agent.raiseIntent("intentName", context);
@@ -366,11 +704,50 @@ try {
} else {
console.error(`${resolution.source} didn't return anything`);
}
-} catch(error) {
+} catch(error: ResultError) {
console.error(`${resolution.source} returned an error: ${error}`);
}
```
+
+
+
+```csharp
+var resolution = await _desktopAgent.RaiseIntent("QuoteStream", new Instrument(new InstrumentID() { Ticker = "AAPL" }));
+try
+{
+ var result = await resolution.GetResult();
+
+ //check that we got a result and that it's a channel
+ if (result is IChannel channel)
+ {
+ var listener = await channel.AddContextListener("price", (quote, metadata) => System.Diagnostics.Debug.WriteLine(quote));
+
+ //if it's a PrivateChannel
+ if (channel is IPrivateChannel privateChannel)
+ {
+ privateChannel.OnDisconnect(() => {
+ System.Diagnostics.Debug.WriteLine("Quote feed went down");
+ });
+
+ // Sometime later...
+ listener.Unsubscribe();
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine($" {resolution.Source} did not return a channel");
+ }
+}
+catch (Exception ex)
+{
+ // Handle exception
+}
+```
+
+
+
+
**See also:**
- [`IntentResult`](Types#intentresult)
diff --git a/docs/api/ref/PrivateChannel.md b/docs/api/ref/PrivateChannel.md
index 5fc8e9c55..07168866d 100644
--- a/docs/api/ref/PrivateChannel.md
+++ b/docs/api/ref/PrivateChannel.md
@@ -4,6 +4,10 @@ sidebar_label: PrivateChannel
title: PrivateChannel
hide_title: true
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
# `PrivateChannel`
Object representing a private context channel, which is intended to support secure communication between applications, and extends the `Channel` interface with event handlers which provide information on the connection state of both parties, ensuring that desktop agents do not need to queue or retain messages that are broadcast before a context listener is added and that applications are able to stop broadcasting messages when the other party has disconnected.
@@ -14,6 +18,9 @@ It is intended that Desktop Agent implementations:
- MUST prevent `PrivateChannels` from being retrieved via fdc3.getOrCreateChannel.
- MUST provide the `id` value for the channel as required by the `Channel` interface.
+
+
+
```ts
interface PrivateChannel extends Channel {
// methods
@@ -24,6 +31,22 @@ interface PrivateChannel extends Channel {
}
```
+
+
+
+```csharp
+interface IPrivateChannel : IChannel, IIntentResult
+{
+ IListener OnAddContextListener(Action handler);
+ IListener OnUnsubscribe(Action handler);
+ IListener OnDisconnect(Action handler);
+ void Disconnect();
+}
+```
+
+
+
+
**See also:**
- [`Channel`](Channel)
@@ -42,7 +65,10 @@ The Desktop Agent knows that a channel is being returned by inspecting the objec
Although this interaction occurs entirely in frontend code, we refer to it as the 'server-side' interaction as it receives a request and initiates a stream of responses.
-```typescript
+
+
+
+```ts
fdc3.addIntentListener("QuoteStream", async (context) => {
const channel: PrivateChannel = await fdc3.createPrivateChannel();
const symbol = context.id.ticker;
@@ -69,13 +95,49 @@ fdc3.addIntentListener("QuoteStream", async (context) => {
});
```
+
+
+
+```csharp
+_desktopAgent.AddIntentListener("QuoteStream", async (context, metadata) => {
+ var channel = await _desktopAgent.CreatePrivateChannel();
+ var symbol = context?.ID?.Ticker;
+
+ // This gets called when the remote side adds a context listener
+ var addContextListener = channel.OnAddContextListener((contextType) => {
+ // broadcast price quotes as they come in from our quote feed
+ _feed.OnQuote(symbol, (price) => {
+ channel.Broadcast(new Price(price));
+ });
+ });
+
+ // This gets called when the remote side calls Listener.unsubscribe()
+ var unsubscribeListener = channel.OnUnsubscribe((contextType) => {
+ _feed.Stop(symbol);
+ });
+
+ // This gets called if the remote side closes
+ var disconnectListener = channel.OnDisconnect(() => {
+ _feed.stop(symbol);
+ });
+
+ return channel;
+});
+```
+
+
+
+
### '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.
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.
-```javascript
+
+
+
+```ts
try {
const resolution3 = await fdc3.raiseIntent("QuoteStream", { type: "fdc3.instrument", id : { symbol: "AAPL" } });
try {
@@ -96,22 +158,73 @@ try {
} else {
console.warn(`${resolution3.source} did not return a channel`);
}
- } catch(channelError) {
- console.log(`Error: ${resolution3.source} returned an error: ${channelError}`);
+ } catch(resultError: ResultError) {
+ console.log(`Error: ${resolution3.source} returned an error: ${resultError}`);
}
-} catch (resolverError) {
+} catch (resolverError: ResolveError) {
console.error(`Error: Intent was not resolved: ${resolverError}`);
}
```
+
+
+
+```csharp
+var resolution = await _desktopAgent.RaiseIntent("QuoteStream", new Instrument(new InstrumentID() { Ticker = "AAPL" }));
+try
+{
+ var result = await resolution.GetResult();
+
+ //check that we got a result and that it's a channel
+ if (result is IChannel channel)
+ {
+ var listener = await channel.AddContextListener("price", (quote, metadata) => System.Diagnostics.Debug.WriteLine(quote));
+
+ //if it's a PrivateChannel
+ if (channel is IPrivateChannel privateChannel)
+ {
+ privateChannel.OnDisconnect(() => {
+ System.Diagnostics.Debug.WriteLine("Quote feed went down");
+ });
+
+ // Sometime later...
+ listener.Unsubscribe();
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine($" {resolution.Source} did not return a channel");
+ }
+}
+catch (Exception ex)
+{
+ // Handle exception
+}
+```
+
+
+
+
## Methods
### `onAddContextListener`
+
+
+
```ts
onAddContextListener(handler: (contextType?: string) => void): Listener;
```
+
+
+
+```csharp
+IListener OnAddContextListener(Action handler);
+```
+
+
+
Adds a listener that will be called each time that the remote app invokes addContextListener on this channel.
Desktop Agents MUST call this for each invocation of addContextListener on this channel, including those that occurred before this handler was registered (to prevent race conditions).
@@ -122,10 +235,22 @@ Desktop Agents MUST call this for each invocation of addContextListener on this
### `onUnsubscribe`
+
+
+
```ts
onUnsubscribe(handler: (contextType?: string) => void): Listener;
```
+
+
+
+```csharp
+IListener OnUnsubscribe(Action handler);
+```
+
+
+
Adds a listener that will be called whenever the remote app invokes `Listener.unsubscribe()` on a context listener that it previously added.
Desktop Agents MUST call this when disconnect() is called by the other party, for each listener that they had added.
@@ -136,10 +261,22 @@ Desktop Agents MUST call this when disconnect() is called by the other party, fo
### `onDisconnect`
+
+
+
```ts
onDisconnect(handler: () => void): Listener;
```
+
+
+
+```csharp
+IListener OnDisconnect(Action handler);
+```
+
+
+
Adds a listener that will be called when the remote app terminates, for example when its window is closed or because disconnect was called. This is in addition to calls that will be made to onUnsubscribe listeners.
**See also:**
@@ -148,10 +285,22 @@ Adds a listener that will be called when the remote app terminates, for example
### `disconnect`
+
+
+
```ts
disconnect(): void;
```
+
+
+
+```csharp
+void Disconnect();
+```
+
+
+
May be called to indicate that a participant will no longer interact with this channel.
After this function has been called, Desktop Agents SHOULD prevent apps from broadcasting on this channel and MUST automatically call Listener.unsubscribe() for each listener that they've added (causing any `onUnsubscribe` handler added by the other party to be called) before triggering any onDisconnect handler added by the other party.
diff --git a/docs/api/ref/Types.md b/docs/api/ref/Types.md
index 48af5671f..16126d5ec 100644
--- a/docs/api/ref/Types.md
+++ b/docs/api/ref/Types.md
@@ -2,6 +2,9 @@
title: Types
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
FDC3 API operations make use of several type declarations.
## `AppIdentifier`
@@ -10,6 +13,9 @@ Identifies an application, or instance of an application, and is used to target
Will always include at least an `appId` property, which can be used with `fdc3.open`, `fdc3.raiseIntent` etc..
If the `instanceId` field is set then the `AppIdentifier` object represents a specific instance of the application that may be addressed using that Id.
+
+
+
```ts
interface AppIdentifier {
/** The unique application identifier located within a specific application
@@ -30,6 +36,27 @@ interface AppIdentifier {
}
```
+
+
+
+```csharp
+interface IAppIdentifier
+{
+ ///
+ /// The unique application identifier located within a specific application directory instance. An example of an appId might be 'app@sub.root'.
+ ///
+ string AppId { get; }
+
+ ///
+ /// An optional instance identifier, indicating that this object represents a specific instance of the application described.
+ ///
+ string? InstanceId { get; }
+}
+```
+
+
+
+
**See also:**
- [`AppMetadata`](Metadata#appmetadata)
@@ -40,7 +67,10 @@ interface AppIdentifier {
## `Context`
-```typescript
+
+
+
+```ts
interface Context {
id?: { [key: string]: string };
name?: string;
@@ -48,6 +78,33 @@ interface Context {
}
```
+
+
+
+```csharp
+interface IContext: IIntentResult, IDynamicContext where T : class
+{
+ T? ID { get; }
+ string? Name { get; }
+ string Type { get; }
+}
+
+interface IContext : IContext
+
+
The base interface that all contexts should extend: a context data object adhering to the [FDC3 Context Data specification](../../context/spec).
This means that it must at least have a `type` property that indicates what type of data it represents, e.g. `'fdc3.contact'`. The `type` property of context objects is important for certain FDC3 operations, like [`Channel.getCurrentContext`](Channel#getCurrentContext) and [`DesktopAgent.addContextListener`](DesktopAgent#addContextListener), which allows you to filter contexts by their type.
@@ -69,10 +126,23 @@ This means that it must at least have a `type` property that indicates what type
## `ContextHandler`
-```typescript
+
+
+
+```ts
type ContextHandler = (context: Context, metadata?: ContextMetadata) => void;
```
+
+
+
+```csharp
+delegate void ContextHandler(T context, IContextMetadata? metadata = null) where T : IContext;
+```
+
+
+
+
Describes a callback that handles a context event. Optional metadata about the context message, including the app that originated the message, may be provided.
Used when attaching listeners for context broadcasts.
@@ -88,7 +158,10 @@ Optional metadata about the context message, including the app that originated t
## `DesktopAgentIdentifier`
-```typescript
+
+
+
+```ts
/** @experimental */
interface DesktopAgentIdentifier {
/** Used in Desktop Agent Bridging to attribute or target a message to a
@@ -97,6 +170,17 @@ interface DesktopAgentIdentifier {
}
```
+
+
+
+```
+Not implemented
+```
+
+
+
+
+
(Experimental) 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.
**See also:**
@@ -105,10 +189,23 @@ interface DesktopAgentIdentifier {
## `IntentHandler`
-```typescript
+
+
+
+```ts
type IntentHandler = (context: Context, metadata?: ContextMetadata) => Promise | void;
```
+
+
+
+```csharp
+delegate Task IntentHandler(T context, IContextMetadata? metadata = null) where T : IContext;
+```
+
+
+
+
Describes a callback that handles a context event and may return a promise of a Context, Channel object or `void` to be returned to the application that raised the intent.
Used when attaching listeners for raised intents.
@@ -125,10 +222,23 @@ Optional metadata about the intent & context message, including the app that ori
## `IntentResult`
-```typescript
+
+
+
+```ts
type IntentResult = Context | Channel | void;
```
+
+
+
+```csharp
+interface IIntentResult { /* Marker interface implemented by IContext and Channel */ }
+```
+
+
+
+
Describes results that an Intent handler may return that should be communicated back to the app that raised the intent, via the [`IntentResolution`](Metadata#intentresolution).
Represented as a union type in TypeScript, however, this type may be rendered as an interface in other languages that both the `Context` and `Channel` types implement, allowing either to be returned by an `IntentHandler`.
@@ -146,18 +256,47 @@ Represented as a union type in TypeScript, however, this type may be rendered as
A Listener object is returned when an application subscribes to intents or context broadcasts via the [`addIntentListener`](DesktopAgent#addintentlistener) or [`addContextListener`](DesktopAgent#addcontextlistener) methods on the [DesktopAgent](DesktopAgent) object.
-```typescript
+
+
+
+```ts
interface Listener {
unsubscribe(): void;
}
```
+
+
+
+```csharp
+interface IListener
+{
+ void Unsubscribe();
+}
+```
+
+
+
+
### `unsubscribe`
+
+
+
```ts
unsubscribe(): void;
```
+
+
+
+```csharp
+void Unsubscribe();
+```
+
+
+
+
Allows an application to unsubscribe from listening to intents or context broadcasts.
**See also:**
diff --git a/docs/api/spec.md b/docs/api/spec.md
index 7c944e836..7cc59bda5 100644
--- a/docs/api/spec.md
+++ b/docs/api/spec.md
@@ -4,6 +4,9 @@ sidebar_label: Overview
title: API Overview (next)
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
The role of FDC3 API is to establish a baseline interface for interoperability between applications. Because FDC3 is largely an agreement between existing platforms and applications, standards should be optimized for ease of adoption rather than functional completeness. Functionality absent from a FDC3 specification is in no way a commentary on its importance.
The following sections examine the API's use-cases and core concepts. The API is fully defined in both subsequent pages of this Part and a full set of TypeScript definitions in the [src](https://github.com/finos/FDC3/tree/main/src/api) directory of the [FDC3 GitHub repository](https://github.com/finos/FDC3/).
@@ -54,23 +57,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.
-### API Access
-
-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.
-
-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:
-
-```js
-function fdc3Stuff() {
- // Make fdc3 API calls here
-}
-
-if (window.fdc3) {
- fdc3Stuff();
-} else {
- window.addEventListener('fdc3Ready', fdc3Stuff);
-}
-```
+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
@@ -103,8 +90,8 @@ 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`](#api-access).
-- Provide a global [`fdc3Ready`](#api-access) event that is fired when the API is ready for use.
+- 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.
@@ -188,7 +175,10 @@ An application may wish to retrieve information about the version of the FDC3 St
Since version 1.2 of the FDC3 Standard it may do so via the [`fdc3.getInfo()`](ref/DesktopAgent#getinfo) function. The metadata returned can be used, for example, to vary the behavior of an application based on the version supported by the Desktop Agent, e.g.:
-```js
+
+
+
+```ts
import {compareVersionNumbers, versionIsAtLeast} from '@finos/fdc3';
if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), '1.2')) {
@@ -198,14 +188,39 @@ if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), '1.2')) {
}
```
+
+
+
+```csharp
+var version = (await _desktopAgent.GetInfo()).Fdc3Version;
+```
+
+
+
+
The [`ImplementationMetadata`](ref/Metadata#implementationmetadata) object returned also includes the metadata for the calling application, according to the Desktop Agent. This allows the application to retrieve its own `appId`, `instanceId` and other details, e.g.:
-```js
+
+
+
+```ts
let implementationMetadata = await fdc3.getInfo();
let {appId, instanceId} = implementationMetadata.appMetadata;
```
+
+
+
+```csharp
+var implementationMetadata = await _desktopAgent.GetInfo();
+var appId = implementationMetadata.AppMetadata.AppId;
+var instanceId = implementationMetadata.AppMetadata.InstanceId;
+```
+
+
+
+
### Reference apps or app instance(s) and retrieve their metadata
To construct workflows between applications, you need to be able to reference specific applications and instances of those applications.
@@ -242,7 +257,10 @@ An optional result type is also supported when programmatically resolving an int
Successful delivery of an intent depends first upon the Desktop Agent's ability to "resolve the intent" (i.e. map the intent to a specific App instance). Where the target application is ambiguous (because there is more than one application that could resolve the intent and context) Desktop Agents may resolve intents by any suitable methodology. A common method is to display a UI that allows the user to pick the desired App from a list of those that will accept the intent and context. Alternatively, the app issuing the intent may proactively handle resolution by calling [`findIntent`](ref/DesktopAgent#findintent) or [`findIntentByContext`](ref/DesktopAgent#findintentbycontext) and then raise the intent with a specific target application, e.g.:
-```js
+
+
+
+```ts
// Find apps to resolve an intent to start a chat with a given contact
const appIntent = await fdc3.findIntent("StartChat", context);
// use the returned AppIntent object to target one of the returned
@@ -281,6 +299,57 @@ const appIntents = await fdc3.findIntentByContext(context);
await fdc3.raiseIntent(appIntent[0].intent, context, appIntent[0].apps[0]);
```
+
+
+
+```csharp
+// Find apps to resolve an intent to start a chat with a given contact
+var appIntent = await _desktopAgent.FindIntent("StartChat", context);
+// use the returned AppIntent object to target one of the returned
+// chat apps or app instances using the AppMetadata object
+await _desktopAgent.RaiseIntent("StartChat", context, appIntent.Apps.First());
+
+//Find apps to resolve an intent and return a specified context type
+var appIntent = await _desktopAgent.FindIntent("ViewContact", context, "fdc3.contact");
+var resolution = await _desktopAgent.RaiseIntent(appIntent.Intent.Name, context, appIntent.Apps.First());
+try
+{
+ var result = await resolution.GetResult();
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned ${result}");
+}
+catch (Exception ex)
+{
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned an error");
+}
+
+//Find apps to resolve an intent and return a channel
+var appIntent = await _desktopAgent.FindIntent("QuoteStream", context, "channel");
+try
+{
+ var resolution = await _desktopAgent.RaiseIntent(appIntent.Intent.Name, context, appIntent.Apps.First());
+ var result = await resolution.GetResult();
+ if (result is IChannel resolvedChannel)
+ {
+ await resolvedChannel.AddContextListener(null, (context, metadata) => { });
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine("Did not return a channel");
+ }
+}
+catch (Exception ex)
+{
+}
+
+//Find apps that can perform any intent with the specified context
+var appIntents = await _desktopAgent.FindIntentsByContext(context);
+//use the returned AppIntent array to target one of the returned apps
+await _desktopAgent.RaiseIntent(appIntents.First().Intent.Name, context, appIntents.First().Apps.First());
+```
+
+
+
+
Result context types requested are represented by their type name. A channel may be requested by passing the string `"channel"` or a channel that returns a specific type via the syntax `"channel"`, e.g. `"channel"`. Requesting intent resolution to an app returning a channel MUST include apps that are registered as returning a channel with a specific type.
### Intent Resolution
@@ -304,16 +373,36 @@ If the raising of the intent resolves (or rejects), a standard [`IntentResolutio
For example, to raise a specific intent:
-```js
+
+
+
+```ts
try {
const resolution = await fdc3.raiseIntent('StageOrder', context);
}
catch (err){ ... }
```
+
+
+
+```csharp
+try
+{
+ var resolution = await _desktopAgent.RaiseIntent("StageOrder", context);
+}
+catch (Exception ex) { }
+```
+
+
+
+
or to raise an unspecified intent for a specific context, where the user may select an intent from a resolver dialog:
-```js
+
+
+
+```ts
try {
const resolution = await fdc3.raiseIntentForContext(context);
if (resolution.data) {
@@ -323,9 +412,30 @@ try {
catch (err){ ... }
```
+
+
+
+```csharp
+try
+{
+ var resolution = await _desktopAgent.RaiseIntentForContext(context);
+ if (resolution is IContext resolvedContext)
+ {
+ var orderId = resolvedContext.ID;
+ }
+}
+catch (Exception ex) { }
+```
+
+
+
+
Use metadata about the resolving app instance to target a further intent
-```js
+
+
+
+```ts
try {
const resolution = await fdc3.raiseIntent('StageOrder', context);
...
@@ -336,9 +446,29 @@ try {
catch (err) { ... }
```
+
+
+
+```csharp
+try
+{
+ var resolution = await _desktopAgent.RaiseIntent("StageOrder", context);
+
+ //some time later
+ await _desktopAgent.RaiseIntent("UpdateOrder", context, resolution.Source);
+}
+catch (Exception ex) { }
+```
+
+
+
+
Raise an intent and retrieve either data or a channel from the IntentResolution:
-```js
+
+
+
+```ts
let resolution = await agent.raiseIntent("intentName", context);
try {
const result = await resolution.getResult();
@@ -355,6 +485,38 @@ try {
}
```
+
+
+
+```csharp
+var resolution = await _desktopAgent.RaiseIntent("QuoteStream", new Instrument(new InstrumentID() { Ticker = "AAPL" }));
+try
+{
+ var result = await resolution.GetResult();
+
+ //check that we got a result and that it's a channel
+ if (result is IChannel channel)
+ {
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned a channel with id {channel.Id}");
+ }
+ else if (result is IContext context)
+ {
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned data {context}");
+ }
+ else
+ {
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} didn't return anything");
+ }
+}
+catch (Exception ex)
+{
+ System.Diagnostics.Debug.WriteLine($"{resolution.Source} returned an error");
+}
+```
+
+
+
+
### Register an Intent Handler
Applications need to let the system know the intents they can support. Typically, this SHOULD be done via registration with an [App Directory](../app-directory/spec) by providing `interop.intents.listensFor` metadata. However, Desktop Agent implementations MAY support dynamic registration of an [`IntentHandler`](ref/Types#intenthandler) by an app at runtime (for example, when they add an `IntentListener` via the API) to allow for ad-hoc registration which may be helpful at development time. Although dynamic registration is not part of this specification, a Desktop Agent agent MAY choose to support any number of registration paths.
@@ -435,18 +597,45 @@ There SHOULD always be a clear UX indicator of what channel an app is joined to.
To find a User channel, one calls:
-```js
+
+
+
+```ts
// returns an array of channels
const allChannels = await fdc3.getUserChannels();
const redChannel = allChannels.find(c => c.id === 'red');
```
+
+
+
+```csharp
+var allChannels = await _desktopAgent.GetUserChannels();
+var redChannel = allChannels.Single(c => c.Id == "red");
+```
+
+
+
+
To join a User channel, one calls:
-```js
+
+
+
+```ts
fdc3.joinUserChannel(redChannel.id);
```
+
+
+
+```csharp
+await _desktopAgent.JoinUserChannel(redChannel.Id);
+```
+
+
+
+
Calling `fdc3.broadcast` will now route context to the joined channel.
Channel implementations SHOULD ensure that context messages broadcast by an application on a channel are not delivered back to that same application if they are joined to the channel.
@@ -461,7 +650,7 @@ Desktop Agent implementations SHOULD use the following set of channels, to enabl
Future versions of the FDC3 Standard may support connections between desktop agents, where differing user channel sets may cause user experience issues.
:::
-```javascript
+```ts
const recommendedChannels = [
{
id: 'fdc3.channel.1',
@@ -548,7 +737,10 @@ App Channels are topics dynamically created by applications connected via FDC3.
To get (or create) a [`Channel`](ref/Channel) reference, then interact with it:
-```js
+
+
+
+```ts
const appChannel = await fdc3.getOrCreateChannel('my_custom_channel');
// get the current context of the channel
const current = await appChannel.getCurrentContext();
@@ -558,9 +750,28 @@ await appChannel.addContextListener(null, context => {...});
await appChannel.broadcast(context);
```
+
+
+
+```csharp
+var appChannel = await _desktopAgent.GetOrCreateChannel("my_custom_channel");
+// get the current context of the channel
+var current = await appChannel.GetCurrentContext(null);
+// add a listener
+await appChannel.AddContextListener(null, (context, metadata) => { });
+// broadcast to the channel
+await appChannel.Broadcast(context);
+```
+
+
+
+
An app can still explicitly receive context events on any [`Channel`](ref/Channel), regardless of the channel it is currently joined to.
-```js
+
+
+
+```ts
// check for current fdc3 channel
let joinedChannel = await fdc3.getCurrentChannel()
//current channel is null, as the app is not currently joined to a channel
@@ -577,6 +788,28 @@ joinedChannel = await fdc3.getCurrentChannel()
//current channel is now the 'blue' channel
```
+
+
+
+```csharp
+var joinedChannel = await _desktopAgent.GetCurrentChannel();
+// current channel is null, as the app is not currently joined to a channel
+
+// add a context listener for channels we join
+var listener = await _desktopAgent.AddContextListener(null, (context, metadata) => { });
+
+// retrieve an App Channel and add a listener that is specific to that channel
+var myChannel = await _desktopAgent.GetOrCreateChannel("my_custom_channel");
+var myChannelListener = await myChannel.AddContextListener(null, (context, metadata) => { });
+
+await _desktopAgent.JoinUserChannel("blue");
+joinedChannel = await _desktopAgent.GetCurrentChannel();
+// current channel is now the "blue" channel
+```
+
+
+
+
if another application broadcasts to "my_custom_channel" (by retrieving it and broadcasting to it via `myChannel.broadcast()`) then the broadcast will be received by the specific listener (`myChannelListener`) but NOT by the listener for joined channels (`listener`).
### Private Channels
diff --git a/docs/supported-platforms.md b/docs/api/supported-platforms.md
similarity index 62%
rename from docs/supported-platforms.md
rename to docs/api/supported-platforms.md
index eb12eda4f..497284171 100644
--- a/docs/supported-platforms.md
+++ b/docs/api/supported-platforms.md
@@ -11,6 +11,28 @@ There are two main categories of platform: web and native, both of which are des
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.
+### API Access & Globals
+
+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.
+
+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`](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.
+
### Usage
There are two main ways FDC3 can be used from web applications:
@@ -19,7 +41,7 @@ There are two main ways FDC3 can be used from web applications:
Simply rely on the global object being made available by your desktop agent, and address the API directly:
-```javascript
+```js
function sendData() {
window.fdc3.broadcast({
type: 'fdc3.instrument',
@@ -36,7 +58,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 be 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';
@@ -88,16 +110,16 @@ const listener = await addIntentListener('ViewAnalysis', instrument => {
});
```
-**See also:**
+## Native
+
+### .NET
-- [`fdc3Ready() Function`](api/ref/Globals#fdc3ready-function)
+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).
-## Native
+#### Usage
-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:
+FDC3 offers the [`Finos.Fdc3` NuGet package](https://www.nuget.org/packages/Finos.Fdc3) that can be used by .NET 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.
-- 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/website/docusaurus.config.js b/website/docusaurus.config.js
index 40a70f109..4172ef722 100644
--- a/website/docusaurus.config.js
+++ b/website/docusaurus.config.js
@@ -143,7 +143,7 @@ module.exports={
},
{
"label": "Supported Platforms",
- "to": "docs/supported-platforms"
+ "to": "docs/api/supported-platforms"
},
{
"label": "API Reference",
diff --git a/website/sidebars.json b/website/sidebars.json
index 7d718e860..e21d1cdb0 100644
--- a/website/sidebars.json
+++ b/website/sidebars.json
@@ -10,16 +10,15 @@
"fdc3-compliance",
"fdc3-glossary",
"references",
- "supported-platforms",
{
"type": "category",
"label": "API Part",
"items": [
"api/spec",
+ "api/supported-platforms",
"api/ref/DesktopAgent",
"api/ref/Channel",
"api/ref/PrivateChannel",
- "api/ref/Globals",
"api/ref/Types",
"api/ref/Metadata",
"api/ref/Errors",