Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

871 clarify channels behaviour and fix a markdown issue in API spec #971

Merged
merged 11 commits into from
May 23, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Fixed

* Further clarified the difference between the behavior of User channels and other channel types on join/addContextListener. ([#971](https://github.com/finos/FDC3/pull/971))
kriswest marked this conversation as resolved.
Show resolved Hide resolved

## [FDC3 Standard 2.0](https://github.com/finos/FDC3/compare/v1.2..v2.0) - 2022-07-01

### Added
Expand Down
10 changes: 8 additions & 2 deletions docs/api/ref/Channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ hide_title: true
---
# `Channel`

Represents a context channel that applications can join to share context data.
Represents a context channel that applications can join to share context data and provides functions for interacting with it.

A channel can be either a "User" channel (retrieved with [`getUserChannels`](DesktopAgent#getuserchannels)) or a custom "App" channel (obtained through [`getOrCreateChannel`](DesktopAgent#getorcreatechannel)).
A channel can be either a ["User" channel](../api/spec#joining-user-channels) (retrieved with [`getUserChannels`](DesktopAgent#getuserchannels)), a custom ["App" channel](../api/spec#app-channels) (obtained through [`getOrCreateChannel`](DesktopAgent#getorcreatechannel)) or a ["Private" channel](../api/spec#private-channels) (obtained via an intent result).

:::note

There are differences in behavior when you interact with a User channel via the Desktop Agent interface and the Channel interface. Specifically, when 'joining' a User channel or adding a context listener when already joined to a channel via the `DesktopAgent` interface, existing context (matching the type of the context listener) on the channel is received by the context listener immediately. Whereas, when add a context listener via the Channel interface, context is not received automatically, but may be retrieved manually via the [`getCurrentContext()`](#getcurrentcontext) function.

:::

Channels each have a unique identifier, some display metadata and operations for broadcasting context to other applications, or receiving context from other applications.

Expand Down
4 changes: 2 additions & 2 deletions docs/api/ref/DesktopAgent.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ interface DesktopAgent {
addContextListener(contextType: string | null, handler: ContextHandler): Promise<Listener>;
```

Adds a listener for incoming context broadcasts from the Desktop Agent. 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.
Adds a listener for incoming context broadcasts from the Desktop Agent on the current User Channel. 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 only 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. If this function is called after the app has already joined a channel and the channel already contains context that would be passed to the context listener, then it will be called immediately with that context.

Optional metadata about each context message received, including the app that originated the message, SHOULD be provided by the desktop agent implementation.
Optional metadata about each context message received, including the app that originated the message, SHOULD be provided by the Desktop Agent implementation.

#### Examples

Expand Down
61 changes: 29 additions & 32 deletions docs/api/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Examples of endpoints include:

## Desktop Agent Implementation

The FDC3 API specification consists of interfaces. It is expected that each Desktop Agent will implement these interfaces. A typical implemention would provide implementations for the following interfaces:
The FDC3 API specification consists of interfaces. It is expected that each Desktop Agent will implement these interfaces. A Desktop Agent MUST provide implementations for the following interfaces:

- [`DesktopAgent`](ref/DesktopAgent)
- [`Channel`](ref/Channel)
Expand Down Expand Up @@ -102,7 +102,7 @@ An FDC3 Standard compliant Desktop Agent implementation **MUST**:
- Intent resolution MUST take into account any specified input or return context types
- Requests for resolution to apps returning a channel MUST include any apps that are registered as returning a channel with a specific type.
- Return (JavaScript or platform appropriate) Error Objects with messages from the [`ChannelError`](ref/Errors#channelerror), [`OpenError`](ref/Errors#openerror), [`ResolveError`](ref/Errors#resolveerror) and [`ResultError`](ref/Errors#resulterror) enumerations as appropriate.
- Accept as input and return as output data structures that are compatibile with the interfaces defined in this Standard.
- Accept as input and return as output data structures that are compatible with the interfaces defined in this Standard.
- Include implementations of the following [Desktop Agent](ref/DesktopAgent) API functions, as defined in this Standard:
- [`addContextListener`](ref/DesktopAgent#addcontextlistener)
- [`addIntentListener`](ref/DesktopAgent#addintentlistener)
Expand Down Expand Up @@ -175,7 +175,7 @@ In some cases, an application may want to communicate with a single application

### Retrieve Metadata about the Desktop Agent implementation

An application may wish to retrieve information about the version of the FDC3 Standard supported by a Desktop Agent implementation and the name of the implementation provider.
An application may wish to retrieve information about the version of the FDC3 Standard supported by a Desktop Agent implementation and the name of the implementation provider.

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.:

Expand All @@ -199,7 +199,7 @@ let {appId, instanceId} = implementationMetadata.appMetadata;

### 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.
To construct workflows between applications, you need to be able to reference specific applications and instances of those applications.

From version 2.0 of the FDC3 Standard, Desktop Agent functions that reference or return information about other applications do so via an [`AppIdentifier`](ref/Types#appidentifier) type. [`AppIdentifier`](ref/Types#appidentifier) references specific applications via an `appId` from an [App Directory](../app-directory/overview) record and instances of that application via an `instanceId` assigned by the Desktop Agent.

Expand All @@ -223,7 +223,7 @@ As an alternative to raising a specific intent, you may also raise an unspecifie

### Intent Results

An optional [`IntentResult`](ref/Types#intentresult) may also be returned as output by an application handling an intent. Results maybe either a single `Context` object, or a `Channel` that may be used to send a stream of responses. The [`PrivateChannel`](ref/PrivateChannel) type is provided to support synchronisation of data transmitted over returned channels, by allowing both parties to listen for events denoting subscription and unsubscription from the returned channel. `PrivateChannels` are only retrievable via [raising an intent](ref/DesktopAgent#raiseintent).
An optional [`IntentResult`](ref/Types#intentresult) may also be returned as output by an application handling an intent. Results maybe either a single `Context` object, or a `Channel` that may be used to send a stream of responses. The [`PrivateChannel`](ref/PrivateChannel) type is provided to support synchronization of data transmitted over returned channels, by allowing both parties to listen for events denoting subscription and unsubscription from the returned channel. `PrivateChannels` are only retrievable via [raising an intent](ref/DesktopAgent#raiseintent).

For example, an application handling a `CreateOrder` intent might return a context representing the order and including an ID, allowing the application that raised the intent to make further calls using that ID.

Expand Down Expand Up @@ -358,7 +358,7 @@ Optional metadata about each intent & context message received, including the ap

### Compliance with Intent Standards

Intents represent a contract with expected behaviour if an app asserts that it supports the intent. Where this contract is enforceable by schema (for example, return object types), the FDC3 API implementation SHOULD enforce compliance and return an error if the interface is not met.
Intents represent a contract with expected behavior if an app asserts that it supports the intent. Where this contract is enforceable by schema (for example, return object types), the FDC3 API implementation SHOULD enforce compliance and return an error if the interface is not met.

It is expected that App Directories SHOULD also curate listed apps and ensure that they are complying with declared intents.

Expand All @@ -370,41 +370,38 @@ Context channels allows a set of apps to share a stateful piece of data between

There are three types of channels, which have different visibility and discoverability semantics:

1. **_User channels_**, which:
1. **User channels**, which:
- facilitate the creation of user-controlled context links between applications (often via the selection of a color channel),
- are created and named by the desktop agent,
- are discoverable (via the [`getUserChannels()`](ref/DesktopAgent#getuserchannels) API call),
- can be 'joined' (via the [`joinUserChannel()`](ref/DesktopAgent#joinuserchannel) API call).

* facilitate the creation of user-controlled context links between applications (often via the selection of a color channel),
* are created and named by the desktop agent,
* are discoverable (via the [`getUserChannels()`](ref/DesktopAgent#getuserchannels) API call),
* can be 'joined' (via the [`joinUserChannel()`](ref/DesktopAgent#joinuserchannel) API call).
:::note
Prior to FDC3 2.0, 'user' channels were known as 'system' channels. They were renamed in FDC3 2.0 to reflect their intended usage, rather than the fact that they are created by system (which could also create 'app' channels).
:::

:::note
Prior to FDC3 2.0, 'user' channels were known as 'system' channels. They were renamed in FDC3 2.0 to reflect their intended usage, rather than the fact that they are created by system (which could also create 'app' channels).
:::
:::note
Earlier versions of FDC3 included the concept of a 'global' system channel
which was deprecated in FDC3 1.2 and removed in FDC3 2.0.
:::

:::note
Earlier versions of FDC3 included the concept of a 'global' system channel
which was deprecated in FDC3 1.2 and removed in FDC3 2.0.
:::
2. **App channels**, which:
- facilitate developer controlled messaging between applications,
- are created and named by applications (via the [`getOrCreateChannel()`](ref/DesktopAgent#getorcreatechannel) API call),
- are not discoverable,
- are interacted with via the [Channel API](ref/Channel) (accessed via the desktop agent [`getOrCreateChannel`](ref/DesktopAgent#getorcreatechannel) API call)

2. **_App channels_**, which:

* facilitate developer controlled messaging between applications,
* are created and named by applications (via the [`getOrCreateChannel()`](ref/DesktopAgent#getorcreatechannel) API call),
* are not discoverable,
* are interacted with via the [Channel API](ref/Channel) (accessed via the desktop agent [`getOrCreateChannel`](ref/DesktopAgent#getorcreatechannel) API call)

3. **_Private_** channels, which:

* facilitate private communication between two parties,
* have an auto-generated identity and can only be retrieved via a raised intent.
3. **Private** channels, which:
- facilitate private communication between two parties,
- have an auto-generated identity and can only be retrieved via a raised intent.

Channels are interacted with via `broadcast` and `addContextListener` functions, allowing an application to send and receive Context objects via the channel. For User channels, these functions are provided on the Desktop Agent, e.g. [`fdc3.broadcast(context)`](ref/DesktopAgent#broadcast), and apply to channels joined via [`fdc3.joinUserChannel`](ref/DesktopAgent#joinuserchannel). For App channels, a channel object must be retrieved, via [`fdc3.getOrCreateChannel(channelName)`](ref/DesktopAgent#getorcreatechannel), which provides the functions, i.e. [`myChannel.broadcast(context)`](ref/Channel#broadcast) and [`myChannel.addContextListener(context)`](ref/Channel#addcontextlistener). For `PrivateChannels`, a channel object must also be retrieved, but via an intent raised with [`fdc3.raiseIntent(intent, context)`](ref/DesktopAgent#raiseintent) and returned as an [`IntentResult`](ref/Types#intentresult).

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 also listening on the channel.

### Joining User Channels

Apps can join _User channels_. An app can only be joined to one User channel at a time.
Apps can join *User channels*. An app can only be joined to one User channel at a time.

When an app is joined to a User channel, calls to [`fdc3.broadcast`](ref/DesktopAgent#broadcast) will be routed to that channel and listeners added through [`fdc3.addContextListener`](ref/DesktopAgent#addcontextlistener) will receive context broadcasts from other apps also joined to that channel. If an app is not joined to a User channel [`fdc3.broadcast`](ref/DesktopAgent#broadcast) will be a no-op and handler functions added with [`fdc3.addContextListener`](ref/DesktopAgent#addcontextlistener) will not receive any broadcasts. However, apps can still choose to listen and broadcast to specific channels (both User and App channels) via the methods on the [`Channel`](ref/Channel) class.

Expand Down Expand Up @@ -525,7 +522,7 @@ const recommendedChannels = [

### Direct Listening and Broadcast on Channels

While joining User channels (using fdc3.joinUserChannel) automates a lot of the channel behaviour for an app, it has the limitation that an app can only be 'joined' to one channel at a time. However, an app may instead retrieve a `Channel` Object via the [`fdc3.getOrCreateChannel`](ref/DesktopAgent#getorcreatechannel) API, or by raising an intent that returns a channel. The `Channel` object may then be used to listen to and broadcast on that channel directly using the [`Channel.addContextListener`](ref/Channel#addcontextlistener) and the [`Channel.broadcast`](ref/Channel#broadcast) APIs. This is especially useful for working with dynamic *App Channels*. FDC3 imposes no restriction on adding context listeners or broadcasting to multiple channels.
While joining User channels (using fdc3.joinUserChannel) automates a lot of the channel behavior for an app, it has the limitation that an app can only be 'joined' to one channel at a time. However, an app may instead retrieve a `Channel` Object via the [`fdc3.getOrCreateChannel`](ref/DesktopAgent#getorcreatechannel) API, or by raising an intent that returns a channel. The `Channel` object may then be used to listen to and broadcast on that channel directly using the [`Channel.addContextListener`](ref/Channel#addcontextlistener) and the [`Channel.broadcast`](ref/Channel#broadcast) APIs. This is especially useful for working with dynamic *App Channels*. FDC3 imposes no restriction on adding context listeners or broadcasting to multiple channels.
kriswest marked this conversation as resolved.
Show resolved Hide resolved

### App Channels

Expand Down Expand Up @@ -574,7 +571,7 @@ 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.

The `PrivateChannel` type also supports synchronisation of data transmitted over returned channels. They do so by extending 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.
The `PrivateChannel` type also supports synchronization of data transmitted over returned channels. They do so by extending 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.

### Broadcasting and listening for multiple context types

Expand Down
13 changes: 12 additions & 1 deletion src/api/Channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,18 @@ import { DisplayMetadata } from './DisplayMetadata';
import { Listener } from './Listener';

/**
* Object representing a context channel.
* Represents a context channel that applications can use to send and receive
* context data.
*
* Please note that There are differences in behavior when you interact with a
* User channel via the `DesktopAgent` interface and the `Channel` interface.
* Specifically, when 'joining' a User channel or adding a context listener
* when already joined to a channel via the `DesktopAgent` interface, existing
* context (matching the type of the context listener) on the channel is
* received by the context listener immediately. Whereas, when a context
* listener is added via the Channel interface, context is not received
* automatically, but may be retrieved manually via the `getCurrentContext()`
* function.
*/
export interface Channel {
/**
Expand Down
10 changes: 8 additions & 2 deletions website/versioned_docs/version-2.0/api/ref/Channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ original_id: Channel
---
# `Channel`

Represents a context channel that applications can join to share context data.
Represents a context channel that applications can use to send and receive context data.

A channel can be either a "User" channel (retrieved with [`getUserChannels`](DesktopAgent#getuserchannels)) or a custom "App" channel (obtained through [`getOrCreateChannel`](DesktopAgent#getorcreatechannel)).
A channel can be either a ["User" channel](../api/spec#joining-user-channels) (retrieved with [`getUserChannels`](DesktopAgent#getuserchannels)), a custom ["App" channel](../api/spec#app-channels) (obtained through [`getOrCreateChannel`](DesktopAgent#getorcreatechannel)) or a ["Private" channel](../api/spec#private-channels) (obtained via an intent result).

:::note

There are differences in behavior when you interact with a User channel via the `DesktopAgent` interface and the `Channel` interface. Specifically, when 'joining' a User channel or adding a context listener when already joined to a channel via the `DesktopAgent` interface, existing context (matching the type of the context listener) on the channel is received by the context listener immediately. Whereas, when a context listener is added via the `Channel` interface, context is not received automatically, but may be retrieved manually via the [`getCurrentContext()`](#getcurrentcontext) function.

:::

Channels each have a unique identifier, some display metadata and operations for broadcasting context to other applications, or receiving context from other applications.

Expand Down
Loading