From 380506cc6789157ba5625c1bac8b09730ae580d5 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 6 May 2022 18:52:10 +0100 Subject: [PATCH 01/23] adding interopUse field to appD app record schema --- CHANGELOG.md | 1 + src/app-directory/specification/appd.yaml | 69 ++++++++++++++++++- .../static/schemas/next/app-directory.yaml | 69 ++++++++++++++++++- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 889ad023f..54127f9af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `lang` field to AppD application records to specify the primary language of an app and its appD record. ([#670](https://github.com/finos/FDC3/pull/670)) * Added `localizedVersions` field to AppD application records to support localized versions of descriptive fields in the app records and alternative launch details for localized versions of the applications themselves. ([#670](https://github.com/finos/FDC3/pull/670)) * Added `categories` field and recommended categories list to AppD application records to enable category based browsing of AppDs ([#673](https://github.com/finos/FDC3/pull/673)) +* Added `interopUse` field to AppD application records to description of an apps use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) ### Changed * Consolidated `Listener` documentation with other types ([#404](https://github.com/finos/FDC3/pull/404)) diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 74edb2c82..0a4658772 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -578,12 +578,13 @@ components: intents: type: array description: > - The list of intents implemented by the Application as defined by - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + The list of intents that the Application listens for via `fdc3.addIntentListener()` items: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' + interopUse: + $ref: '#/components/schemas/InteropUse' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -877,6 +878,70 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. + InteropUse: + type: object + description: >- + Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, + which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, + to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application + use to other app developers and desktop assemblers. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API + use MAY vary from what is described. + properties: + raisesIntents: + type: object + description: Intents that are raised by the application and contexts they are raised with. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + raisesContexts: + type: array + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow + the user to select an intent from those that are available via the Desktop Agent. + items: + type: string + userChannels: + type: object + description: Describes the application's use of context types on User Channels + properties: + broadcast: + type: array + description: Context types that are broadcast by the application + items: + type: string + listen: + type: array + description: Context types that the application listens for + items: + type: string + appChannels: + type: array + description: Describes the application's use of App Channels + items: + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index 74edb2c82..0a4658772 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -578,12 +578,13 @@ components: intents: type: array description: > - The list of intents implemented by the Application as defined by - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + The list of intents that the Application listens for via `fdc3.addIntentListener()` items: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' + interopUse: + $ref: '#/components/schemas/InteropUse' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -877,6 +878,70 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. + InteropUse: + type: object + description: >- + Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, + which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, + to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application + use to other app developers and desktop assemblers. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API + use MAY vary from what is described. + properties: + raisesIntents: + type: object + description: Intents that are raised by the application and contexts they are raised with. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + raisesContexts: + type: array + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow + the user to select an intent from those that are available via the Desktop Agent. + items: + type: string + userChannels: + type: object + description: Describes the application's use of context types on User Channels + properties: + broadcast: + type: array + description: Context types that are broadcast by the application + items: + type: string + listen: + type: array + description: Context types that the application listens for + items: + type: string + appChannels: + type: array + description: Describes the application's use of App Channels + items: + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: From 73fcbc2ea68e67c442df56f98b1e5204b0bacc74 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 6 May 2022 18:53:58 +0100 Subject: [PATCH 02/23] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54127f9af..cd2638dc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `lang` field to AppD application records to specify the primary language of an app and its appD record. ([#670](https://github.com/finos/FDC3/pull/670)) * Added `localizedVersions` field to AppD application records to support localized versions of descriptive fields in the app records and alternative launch details for localized versions of the applications themselves. ([#670](https://github.com/finos/FDC3/pull/670)) * Added `categories` field and recommended categories list to AppD application records to enable category based browsing of AppDs ([#673](https://github.com/finos/FDC3/pull/673)) -* Added `interopUse` field to AppD application records to description of an apps use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) +* Added `interopUse` field to AppD application records to describe an app's use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) ### Changed * Consolidated `Listener` documentation with other types ([#404](https://github.com/finos/FDC3/pull/404)) From 085d0483efa564c49cec8ab11088c87f07fa0df4 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 May 2022 11:49:33 +0100 Subject: [PATCH 03/23] Add a recommended set of user channels to the Standard --- CHANGELOG.md | 1 + docs/api/spec.md | 85 +++++++++++++++++- src/api/RecommendedChannels.ts | 90 +++++++++++++++++++ src/index.ts | 1 + .../static/schemas/next/app-directory.yaml | 2 +- 5 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 src/api/RecommendedChannels.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 863dc38ac..3c17224db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added details of FDC3's existing versioning and deprecation policies to the FDC3 compliance page ([#539](https://github.com/finos/FDC3/pull/539)) * Added a new experimental features policy, which exempts features designated as experimental from the versioning and deprecation policies, to the FDC3 compliance page ([#549](https://github.com/finos/FDC3/pull/549)) * Add `IntentDeliveryFailed` to the `ResolveError` enumeration to be used when delivery of an intent and context to a targetted app or instance fails. ([#601](https://github.com/finos/FDC3/pull/601)) +* Added a recommended set of user channel definitions to the API docs and typescript sources ([#727](https://github.com/finos/FDC3/pull/726)) ### Changed * Consolidated `Listener` documentation with other types ([#404](https://github.com/finos/FDC3/pull/404)) diff --git a/docs/api/spec.md b/docs/api/spec.md index f1812b2f4..7a1229332 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -332,7 +332,90 @@ 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. - > Prior to FDC3 2.0, 'user' channels were known as 'system' channels. They were renamed in FDC 2.0 to reflect their intended usage, rather than the fact that they are created by system (which could also create 'app' channels). The `joinChannel` function was also renamed to `joinUserChannel` to clarify that it is only intended to be used to join 'user', rather than 'app', channels. + > 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). The `joinChannel` function was also renamed to `joinUserChannel` to clarify that it is only intended to be used to join 'user', rather than 'app', channels. + +### Recommended User Channel Set + +Desktop Agent implementations SHOULD use the following set of channels, to enable a consistent user experience across different implementations. Desktop Agent implementation MAY support configuration of the user channels. + +> Note: Future versions of the FDC3 Standard may support connections between desktop agents, where differing user channel sets may cause user experience issues. + +```javascript +const recommendedChannels = [ + { + "id": "A", + "type": "user", + "displayMetadata": { + "name": "Channel A", + "color": "red", + "glyph": "A" + } + }, + { + "id": "B", + "type": "user", + "displayMetadata": { + "name": "Channel B", + "color": "orange", + "glyph": "B" + } + }, + { + "id": "C", + "type": "user", + "displayMetadata": { + "name": "Channel C", + "color": "yellow", + "glyph": "C" + } + }, + { + "id": "D", + "type": "user", + "displayMetadata": { + "name": "Channel D", + "color": "green", + "glyph": "D" + } + }, + { + "id": "E", + "type": "user", + "displayMetadata": { + "name": "Channel E", + "color": "lightblue", + "glyph": "E" + } + }, + { + "id": "F", + "type": "user", + "displayMetadata": { + "name": "Channel F", + "color": "blue", + "glyph": "F" + } + }, + { + "id": "G", + "type": "user", + "displayMetadata": { + "name": "Channel G", + "color": "purple", + "glyph": "G" + } + }, + { + "id": "H", + "type": "user", + "displayMetadata": { + "name": "Channel H", + "color": "brown", + "glyph": "H" + } + } +]; +``` ### 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. diff --git a/src/api/RecommendedChannels.ts b/src/api/RecommendedChannels.ts new file mode 100644 index 000000000..f3dcbcfd6 --- /dev/null +++ b/src/api/RecommendedChannels.ts @@ -0,0 +1,90 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + * Copyright FINOS FDC3 contributors - see NOTICE file + */ + +import { DisplayMetadata } from './DisplayMetadata'; + +/** Interface representing the data fields of a user channel, without the functions. */ +interface UserChannelTemplate { + readonly id: string; + readonly type: 'user'; + readonly displayMetadata?: DisplayMetadata; +} + +const recommendedChannels: Array = [ + { + id: 'A', + type: 'user', + displayMetadata: { + name: 'Channel A', + color: 'red', + glyph: 'A', + }, + }, + { + id: 'B', + type: 'user', + displayMetadata: { + name: 'Channel B', + color: 'orange', + glyph: 'B', + }, + }, + { + id: 'C', + type: 'user', + displayMetadata: { + name: 'Channel C', + color: 'yellow', + glyph: 'C', + }, + }, + { + id: 'D', + type: 'user', + displayMetadata: { + name: 'Channel D', + color: 'green', + glyph: 'D', + }, + }, + { + id: 'E', + type: 'user', + displayMetadata: { + name: 'Channel E', + color: 'lightblue', + glyph: 'E', + }, + }, + { + id: 'F', + type: 'user', + displayMetadata: { + name: 'Channel F', + color: 'blue', + glyph: 'F', + }, + }, + { + id: 'G', + type: 'user', + displayMetadata: { + name: 'Channel G', + color: 'purple', + glyph: 'G', + }, + }, + { + id: 'H', + type: 'user', + displayMetadata: { + name: 'Channel H', + color: 'brown', + glyph: 'H', + }, + }, +]; + +export default recommendedChannels; diff --git a/src/index.ts b/src/index.ts index 6209bae89..54215c3ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ export * from './api/IntentResolution'; export * from './api/Listener'; export * from './api/ImplementationMetadata'; export * from './api/Methods'; +export * from './api/RecommendedChannels'; export * from './context/ContextType'; export * from './context/ContextTypes'; export * from './intents/Intents'; diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index e7261c4c6..bece799ed 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -378,7 +378,7 @@ components: type: string description: >- A comma separated list of the types of contexts the intent offered by the application can process, - where the first part of the context type is the namespace e.g."fdc3.contact", "org.symphony.contact" + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" resultType: type: string description: >- From aaad6d5c42efe12e77df1ef9190372f87721b7ce Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 20 May 2022 17:56:34 +0100 Subject: [PATCH 04/23] move new elements to top-level of schema --- src/app-directory/specification/appd.yaml | 141 +++++++++-------- .../static/schemas/next/app-directory.yaml | 145 ++++++++++-------- 2 files changed, 164 insertions(+), 122 deletions(-) diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 7c0c1fb8f..559860f52 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -428,8 +428,14 @@ components: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - interopUse: - $ref: '#/components/schemas/InteropUse' + raiseIntent: + $ref: '#/components/schemas/RaiseIntent' + raiseIntentForContext: + $ref: '#/components/schemas/RaiseIntentForContext' + userChannels: + $ref: '#/components/schemas/UserChannels' + appChannels: + $ref: '#/components/schemas/AppChannels' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -543,9 +549,13 @@ components: $ref: '#/components/schemas/NameValuePair' intents: type: array - description: > - The list of intents implemented by the Application as defined by + description: >- + The list of intents that the Application listens for and resolves as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + Primarily used to implement intent resolution in a Desktop Agent, but may also be used, + for example in an app catalog UI, to find apps that 'interoperate with' other apps. + An Application MUST use the Desktop Agent's `addIntentListener` API to add an intent listener + for each intent listed here as soon as possible after it starts up. items: $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: @@ -785,70 +795,81 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - InteropUse: + RaiseIntent: type: object description: >- - Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, - which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, - to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application - use to other app developers and desktop assemblers. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API - use MAY vary from what is described. + Optional metadata that describes which Intents are raised by the application and context types they are + raised with. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise intents that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + RaiseIntentForContext: + type: array + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow the user to + select an intent from those that are available via the Desktop Agent. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise intents that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + additionalProperties: + items: + type: string + UserChannels: + type: object + description: >- + Describes the application's use of context types on User Channels. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise contexts that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. properties: - raisesIntents: - type: object - description: Intents that are raised by the application and contexts they are raised with. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - raisesContexts: + broadcast: type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow - the user to select an intent from those that are available via the Desktop Agent. + description: Context types that are broadcast by the application items: type: string - userChannels: - type: object - description: Describes the application's use of context types on User Channels - properties: - broadcast: - type: array - description: Context types that are broadcast by the application - items: - type: string - listen: - type: array - description: Context types that the application listens for - items: - type: string - appChannels: + listen: type: array - description: Describes the application's use of App Channels + description: Context types that the application listens for items: - type: object - required: - - name - properties: - name: - type: string - description: The name of the App Channel - description: - type: string - description: A description of how the channel is used - broadcast: - type: array - description: Context types that are broadcast by the application on the channel - items: - type: string - listen: - type: array - description: Context types that the application listens for on the channel - items: - type: string + type: string + AppChannels: + type: array + description: >- + Describes the application's use of App Channels. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise App channels and contexts that the application creates and uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + items: + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index db3ff3060..559860f52 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -428,8 +428,14 @@ components: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - interopUse: - $ref: '#/components/schemas/InteropUse' + raiseIntent: + $ref: '#/components/schemas/RaiseIntent' + raiseIntentForContext: + $ref: '#/components/schemas/RaiseIntentForContext' + userChannels: + $ref: '#/components/schemas/UserChannels' + appChannels: + $ref: '#/components/schemas/AppChannels' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -543,9 +549,13 @@ components: $ref: '#/components/schemas/NameValuePair' intents: type: array - description: > - The list of intents implemented by the Application as defined by + description: >- + The list of intents that the Application listens for and resolves as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + Primarily used to implement intent resolution in a Desktop Agent, but may also be used, + for example in an app catalog UI, to find apps that 'interoperate with' other apps. + An Application MUST use the Desktop Agent's `addIntentListener` API to add an intent listener + for each intent listed here as soon as possible after it starts up. items: $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: @@ -643,7 +653,7 @@ components: type: string description: >- A comma separated list of the types of contexts the intent offered by the application can process, - where the first part of the context type is the namespace e.g."fdc3.contact", "org.symphony.contact" + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" resultType: type: string description: >- @@ -704,7 +714,7 @@ components: LaunchDetails: description: >- The type specific launch details of the application. These details are intended to be - vendor-agnostic and _MAY_ be duplicated or overridden by details provided in the hostManifests + vendor-agnostic and MAY be duplicated or overridden by details provided in the hostManifests object for a specific host. oneOf: - $ref: '#/components/schemas/WebAppDetails' @@ -785,70 +795,81 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - InteropUse: + RaiseIntent: type: object description: >- - Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, - which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, - to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application - use to other app developers and desktop assemblers. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API - use MAY vary from what is described. + Optional metadata that describes which Intents are raised by the application and context types they are + raised with. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise intents that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + RaiseIntentForContext: + type: array + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow the user to + select an intent from those that are available via the Desktop Agent. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise intents that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + additionalProperties: + items: + type: string + UserChannels: + type: object + description: >- + Describes the application's use of context types on User Channels. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise contexts that the application uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. properties: - raisesIntents: - type: object - description: Intents that are raised by the application and contexts they are raised with. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - raisesContexts: + broadcast: type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow - the user to select an intent from those that are available via the Desktop Agent. + description: Context types that are broadcast by the application items: type: string - userChannels: - type: object - description: Describes the application's use of context types on User Channels - properties: - broadcast: - type: array - description: Context types that are broadcast by the application - items: - type: string - listen: - type: array - description: Context types that the application listens for - items: - type: string - appChannels: + listen: type: array - description: Describes the application's use of App Channels + description: Context types that the application listens for items: - type: object - required: - - name - properties: - name: - type: string - description: The name of the App Channel - description: - type: string - description: A description of how the channel is used - broadcast: - type: array - description: Context types that are broadcast by the application on the channel - items: - type: string - listen: - type: array - description: Context types that the application listens for on the channel - items: - type: string + type: string + AppChannels: + type: array + description: >- + Describes the application's use of App Channels. + This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other + apps or to advertise App channels and contexts that the application creates and uses. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage + MAY vary from what is described. + items: + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: From 0ca63a3146ca52bbac10f8a2b6cfa9d5e0f945c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 May 2022 20:56:10 +0000 Subject: [PATCH 05/23] Bump gson from 2.2.4 to 2.8.9 in /src/app-directory Bumps [gson](https://github.com/google/gson) from 2.2.4 to 2.8.9. - [Release notes](https://github.com/google/gson/releases) - [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/gson/compare/gson-2.2.4...gson-parent-2.8.9) --- updated-dependencies: - dependency-name: com.google.code.gson:gson dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/app-directory/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app-directory/pom.xml b/src/app-directory/pom.xml index 59fa822f2..37426ba95 100644 --- a/src/app-directory/pom.xml +++ b/src/app-directory/pom.xml @@ -27,7 +27,7 @@ 2.3.1 false 9.2.9.v20150224 - 2.2.4 + 2.8.9 LATEST From 4bec20ebd3c0082b886150eade70ca27f5419538 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 23 May 2022 17:42:05 +0100 Subject: [PATCH 06/23] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e41adad49..f6d124d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `localizedVersions` field to AppD application records to support localized versions of descriptive fields in the app records and alternative launch details for localized versions of the applications themselves. ([#670](https://github.com/finos/FDC3/pull/670)) * Added `type` and `details` elements to AppD application records to support vendor-agnostic launch details for both web and native apps ([#671](https://github.com/finos/FDC3/pull/671)) * Added `categories` field and recommended categories list to AppD application records to enable category based browsing of AppDs ([#673](https://github.com/finos/FDC3/pull/673)) -* Added `interopUse` field to AppD application records to describe an app's use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) +* Added `raiseIntent`, `raiseIntentForContext`, `appChannels`, and `userChannels` fields to AppD application records to describe an app's use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) ### Changed * Consolidated `Listener` documentation with other types ([#404](https://github.com/finos/FDC3/pull/404)) From 5c4de541b47e21e2212e861c78cf682441ddada4 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 May 2022 12:04:34 +0100 Subject: [PATCH 07/23] Switching to arabic numerals for channel names + losing brown in favour of turquoise --- docs/api/spec.md | 131 +++++++++++++++++---------------- src/api/RecommendedChannels.ts | 56 +++++++------- 2 files changed, 94 insertions(+), 93 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 7a1229332..7e42232a5 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -342,82 +342,83 @@ Desktop Agent implementations SHOULD use the following set of channels, to enabl ```javascript const recommendedChannels = [ - { - "id": "A", - "type": "user", - "displayMetadata": { - "name": "Channel A", - "color": "red", - "glyph": "A" - } + { + id: 'Channel 1', + type: 'user', + displayMetadata: { + name: 'Channel 1', + color: 'red', + glyph: '1', }, - { - "id": "B", - "type": "user", - "displayMetadata": { - "name": "Channel B", - "color": "orange", - "glyph": "B" - } + }, + { + id: 'Channel 2', + type: 'user', + displayMetadata: { + name: 'Channel 2', + color: 'orange', + glyph: '2', }, - { - "id": "C", - "type": "user", - "displayMetadata": { - "name": "Channel C", - "color": "yellow", - "glyph": "C" - } + }, + { + id: 'Channel 3', + type: 'user', + displayMetadata: { + name: 'Channel 3', + color: 'yellow', + glyph: '3', }, - { - "id": "D", - "type": "user", - "displayMetadata": { - "name": "Channel D", - "color": "green", - "glyph": "D" - } + }, + { + id: 'Channel 4', + type: 'user', + displayMetadata: { + name: 'Channel 4', + color: 'green', + glyph: '4', }, - { - "id": "E", - "type": "user", - "displayMetadata": { - "name": "Channel E", - "color": "lightblue", - "glyph": "E" - } + }, + { + id: 'Channel 5', + type: 'user', + displayMetadata: { + name: 'Channel 5', + color: 'turquoise', + glyph: '5', }, - { - "id": "F", - "type": "user", - "displayMetadata": { - "name": "Channel F", - "color": "blue", - "glyph": "F" - } + }, + { + id: 'Channel 6', + type: 'user', + displayMetadata: { + name: 'Channel 6', + color: 'lightblue', + glyph: '6', }, - { - "id": "G", - "type": "user", - "displayMetadata": { - "name": "Channel G", - "color": "purple", - "glyph": "G" - } + }, + { + id: 'Channel 7', + type: 'user', + displayMetadata: { + name: 'Channel 7', + color: 'blue', + glyph: '7', }, - { - "id": "H", - "type": "user", - "displayMetadata": { - "name": "Channel H", - "color": "brown", - "glyph": "H" - } - } + }, + { + id: 'Channel 8', + type: 'user', + displayMetadata: { + name: 'Channel 8', + color: 'purple', + glyph: '8', + }, + }, ]; ``` ### 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. ### App Channels diff --git a/src/api/RecommendedChannels.ts b/src/api/RecommendedChannels.ts index f3dcbcfd6..e5aab43e9 100644 --- a/src/api/RecommendedChannels.ts +++ b/src/api/RecommendedChannels.ts @@ -14,75 +14,75 @@ interface UserChannelTemplate { const recommendedChannels: Array = [ { - id: 'A', + id: 'Channel 1', type: 'user', displayMetadata: { - name: 'Channel A', + name: 'Channel 1', color: 'red', - glyph: 'A', + glyph: '1', }, }, { - id: 'B', + id: 'Channel 2', type: 'user', displayMetadata: { - name: 'Channel B', + name: 'Channel 2', color: 'orange', - glyph: 'B', + glyph: '2', }, }, { - id: 'C', + id: 'Channel 3', type: 'user', displayMetadata: { - name: 'Channel C', + name: 'Channel 3', color: 'yellow', - glyph: 'C', + glyph: '3', }, }, { - id: 'D', + id: 'Channel 4', type: 'user', displayMetadata: { - name: 'Channel D', + name: 'Channel 4', color: 'green', - glyph: 'D', + glyph: '4', }, }, { - id: 'E', + id: 'Channel 5', type: 'user', displayMetadata: { - name: 'Channel E', - color: 'lightblue', - glyph: 'E', + name: 'Channel 5', + color: 'turquoise', + glyph: '5', }, }, { - id: 'F', + id: 'Channel 6', type: 'user', displayMetadata: { - name: 'Channel F', - color: 'blue', - glyph: 'F', + name: 'Channel 6', + color: 'lightblue', + glyph: '6', }, }, { - id: 'G', + id: 'Channel 7', type: 'user', displayMetadata: { - name: 'Channel G', - color: 'purple', - glyph: 'G', + name: 'Channel 7', + color: 'blue', + glyph: '7', }, }, { - id: 'H', + id: 'Channel 8', type: 'user', displayMetadata: { - name: 'Channel H', - color: 'brown', - glyph: 'H', + name: 'Channel 8', + color: 'purple', + glyph: '8', }, }, ]; From 4666d8cd946801a0dd398f53cfa5017092107114 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 May 2022 12:06:40 +0100 Subject: [PATCH 08/23] mark down lint --- docs/api/spec.md | 87 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index 7e42232a5..fd6728f59 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -9,7 +9,9 @@ The role of FDC3 API is to establish a baseline interface for interoperability b The following sections examine the API's use-cases and core concepts. The APIs a 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/master/src/api) directory of the [FDC3 GitHub repository](https://github.com/finos/FDC3/). ## Components + ### Desktop Agent + A Desktop Agent is a desktop component (or aggregate of components) that serves as a launcher and message router (broker) for applications in its domain. 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. Examples of Desktop Agents include: @@ -23,17 +25,21 @@ Examples of Desktop Agents include: An FDC3-compliant Desktop Agent exposes an FDC3 standard API to applications they have launched. When an App is launched by a Desktop Agent and is given access to the Agent's API to interoperate, it is running in that Desktop Agent's *context*. ### Application + An application is any endpoint on the desktop that is: + - Registered with/known by a Desktop Agent - Launchable by a Desktop Agent - Addressable by a Desktop Agent Examples of End Points include: + - Native Applications - Web Applications - Headless “services” running on the desktop ## 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 instantiable classes for the following interfaces: - [`DesktopAgent`](ref/DesktopAgent) @@ -57,6 +63,7 @@ Other interfaces defined in the spec are not critical to define as concrete type - [`TargetApp`](ref/Types#targetapp) ### 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: @@ -74,11 +81,13 @@ if (window.fdc3) { ``` ### Standards vs. Implementation + ![Desktop Agent - Standards Schematic](assets/api-1.png) The surface area of FDC3 standardization (shown in *white* above) itself is quite small in comparison to the extent of a typical desktop agent implementation (in *grey*). For example: + - workspace management - user identity and SSO - entitlements @@ -87,6 +96,7 @@ For example: Are all areas of functionality that any feature complete desktop agent would implement, but are not currently areas considered for standardization under FDC3. ### Inter-Agent Communication + A goal of FDC3 standards is that applications running in different Desktop Agent contexts on the same desktop would be able to interoperate. And that one Desktop Agent context would be able to discover and launch an application in another Desktop Application context. ![Desktop Agent - Interop](assets/api-2.png) @@ -98,6 +108,7 @@ An actual connection protocol between Desktop Agents is not currently available ## Functional Use Cases ### Retrieve Metadata about the Desktop Agent implementation + From version 1.2 of the FDC3 specification, Desktop Agent implementations MUST provide a `fdc3.getInfo()` function to allow apps to retrieve information about the version of the FDC3 specification supported by a Desktop Agent implementation and the name of the implementation provider. This metadata can be used to vary the behavior of an application based on the version supported by the Desktop Agent, e.g.: ```js @@ -111,11 +122,12 @@ if (fdc3.getInfo && versionIsAtLeast(await fdc3.getInfo(), '1.2')) { ``` ### Open an Application by Name -Linking from one application to another is a critical basic workflow that the web revolutionized via the hyperlink. Supporting semantic addressing of applications across different technologies and platform domains greatly reduces friction in linking different applications into a single workflow. +Linking from one application to another is a critical basic workflow that the web revolutionized via the hyperlink. Supporting semantic addressing of applications across different technologies and platform domains greatly reduces friction in linking different applications into a single workflow. ### Requesting Functionality From Another App -Often, we want to link from one app to another to dynamically create a workflow. Enabling this without requiring prior knowledge between apps is a key goal of FDC3 and is implemented via the raising of [intents](../intents/spec), which represent a desired action, to be performed with a [context](../context/spec) supplied as input. + +Often, we want to link from one app to another to dynamically create a workflow. Enabling this without requiring prior knowledge between apps is a key goal of FDC3 and is implemented via the raising of [intents](../intents/spec), which represent a desired action, to be performed with a [context](../context/spec) supplied as input. Intents provide a way for an app to request functionality from another app and defer the discovery and launching of the destination app to the Desktop Agent. There are multiple models for interop that intents can support. @@ -124,23 +136,27 @@ Intents provide a way for an app to request functionality from another app and d - **Remote API**: An app wants to remote an entire API that it owns to another App. In this case, the API for the App cannot be standardized. However, the FDC3 API can address how an App connects to another App in order to get access to a proprietary API. ### Send/broadcast Context + On the financial desktop, applications often want to broadcast [context](../context/spec) to any number of applications. Context sharing needs to support different groupings of applications, which is supported via the concept of 'channels', over which context is broadcast and received by other applications listening to the channel. In some cases, an application may want to communicate with a single application or service and to prevent other applications from participating in the communication. For single transactions, this can instead be implemented via a raised intent, which will be delivered to a single application that can, optionally, respond with data. Alternatively, it may instead respond with a [`Channel`](ref/Channel) or [`PrivateChannel`](ref/PrivateChannel) over which a stream of responses or a dialog can be supported. ## Raising Intents -Raising an Intent is a method for an application to request functionality from another application and, if desired, defer the discovery and launching of the destination app to the Desktop Agent. + +Raising an Intent is a method for an application to request functionality from another application and, if desired, defer the discovery and launching of the destination app to the Desktop Agent. ### Intents and Context + When raising an intent a specific context is provided as input. The type of the provided context may determine which applications can resolve the intent. -A context type may also be associated with multiple intents. For example, an `fdc3.instrument` could be associated with `ViewChart`, `ViewNews`, `ViewAnalysis` or other intents. +A context type may also be associated with multiple intents. For example, an `fdc3.instrument` could be associated with `ViewChart`, `ViewNews`, `ViewAnalysis` or other intents. To raise an Intent without a context, use the [`fdc3.nothing`](../context/ref/Nothing) context type. This type exists so that applications can explicitly declare that they support raising an intent without a context (when registering an Intent listener or in an App Directory). As an alternative to raising a specific intent, you may also raise an unspecified intent with a known context allowing the Desktop Agent or the user (if the intent is ambiguous) to select the appropriate intent and then to raise it with the specified context for resolution. ### 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). 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. @@ -148,6 +164,7 @@ For example, an application handling a `CreateOrder` intent might return a conte An optional result type is also supported when programmatically resolving an intent via [`findIntent`](ref/DesktopAgent#findintent) or [`findIntentByContext`](ref/DesktopAgent#findintentbycontext). ### Resolvers + 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 @@ -189,22 +206,26 @@ const appIntents = await fdc3.findIntentByContext(context); await fdc3.raiseIntent(appIntent[0].intent, context, appIntent[0].apps[0]); ``` -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. +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 + Raising an intent will return a Promise-type object that will resolve/reject based on a number of factors. #### Resolve + - Intent was resolved unambiguously and the receiving app was launched successfully (if necessary). - Intent was ambiguous, a resolution was chosen by the end user, and the chosen application was launched successfully. #### Reject + - No app matching the intent and context (if specified) was found. - A match was found, but the receiving app failed to launch. - The intent was ambiguous and the resolver experienced an error. #### Resolution Object -If the raising of the intent resolves (or rejects), a standard [`IntentResolution`](ref/Metadata#intentresolution) object will be passed into the resolver function with details of the application that resolved the intent and the means to access any results subsequently returned. + +If the raising of the intent resolves (or rejects), a standard [`IntentResolution`](ref/Metadata#intentresolution) object will be passed into the resolver function with details of the application that resolved the intent and the means to access any results subsequently returned. For example, to raise a specific intent: @@ -228,6 +249,7 @@ catch (err){ ... } ``` Use metadata about the resolving app instance to target a further intent + ```js try { const resolution = await fdc3.raiseIntent('StageOrder', context); @@ -240,6 +262,7 @@ catch (err) { ... } ``` Raise an intent and retrieve either data or a channel from the IntentResolution: + ```js let resolution = await agent.raiseIntent("intentName", context); try { @@ -258,50 +281,53 @@ try { ``` ### Register an Intent Handler + Applications need to let the system know the intents they can support. Typically, this is done via registration with an [App Directory](../app-directory/spec). It is also possible for intents to be registered at the application level as well to support 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. When an instance of an application is launched, it is expected to add an [`IntentHandler`](ref/Types#intenthandler) function to the desktop agent for each intent it has registered by calling the [`fdc3.addIntentListener`](ref/DesktopAgent#addintentlistener) function of the Desktop Agent. Doing so allows the Desktop Agent to pass incoming intents and contexts to that instance of the application. Hence, if the application instance was spawned in response to the raised intent, then the Desktop Agent must wait for the relevant intent listener to be added by that instance, before it can deliver the intent and context to it. In order to facilitate accurate error responses, calls to `fdc3.raiseIntent` should not return an `IntentResolution` until the intent handler has been added and the intent delivered to the target app. #### 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. It is expected that App Directories SHOULD also curate listed apps and ensure that they are complying with declared intents. - ## Context Channels Context channels allows a set of apps to share a stateful piece of data between them, and be alerted when it changes. Use cases for channels include color linking between applications to automate the sharing of context and topic based pub/sub such as theme. ### Types of Channel + There are three types of channels, which have different visibility and discoverability semantics: -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). +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). > **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. -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. @@ -309,11 +335,12 @@ When an app joins a User channel, or adds a context listener when already joined It is possible that a call to join a User channel could be rejected. If for example, the desktop agent wanted to implement controls around what data apps can access. -Joining channels in FDC3 is intended to be a behavior initiated by the end user. For example: by color linking or apps being grouped in the same workspace. Most of the time, it is expected that apps will be joined to a channel by mechanisms outside of the app. To support programmatic management of joined channels and the implementation of channel selector UIs other than those provided outside of the app, Desktop Agent implementations MAY provide [`fdc3.joinChannel()`](ref/DesktopAgent#joinchannel), [`fdc3.getCurrentChannel()](ref/DesktopAgent#getcurrentchannel) and [`fdc3.leaveCurrentChannel()`](ref/DesktopAgent#leavecurrentchannel) functions and if they do, MUST do so as defined in the [Desktop Agent API reference](ref/DesktopAgent). +Joining channels in FDC3 is intended to be a behavior initiated by the end user. For example: by color linking or apps being grouped in the same workspace. Most of the time, it is expected that apps will be joined to a channel by mechanisms outside of the app. To support programmatic management of joined channels and the implementation of channel selector UIs other than those provided outside of the app, Desktop Agent implementations MAY provide [`fdc3.joinChannel()`](ref/DesktopAgent#joinchannel), [`fdc3.getCurrentChannel()](ref/DesktopAgent#getcurrentchannel) and [`fdc3.leaveCurrentChannel()`](ref/DesktopAgent#leavecurrentchannel) functions and if they do, MUST do so as defined in the [Desktop Agent API reference](ref/DesktopAgent). There SHOULD always be a clear UX indicator of what channel an app is joined to. #### Examples + To find a User channel, one calls: ```js @@ -419,9 +446,10 @@ 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 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. ### App Channels + App Channels are topics dynamically created by applications connected via FDC3. For example, an app may` create a channel to broadcast to others data or status specific to that app. To get (or create) a channel reference, then interact with it: @@ -459,17 +487,18 @@ if another application broadcasts to "my_custom_channel" (by retrieving it and b ### Private Channels -`PrivateChannels` are created to support the return of a stream of responses from a raised intent, or private dialog between two applications. +`PrivateChannels` are created to support the return of a stream of responses from a raised intent, or private dialog between two applications. It is intended that Desktop Agent implementations: - * - SHOULD restrict external apps from listening or publishing on this channel. - * - MUST prevent `PrivateChannels` from being retrieved via `fdc3.getOrCreateChannel`. - * - MUST provide the `id` value for the channel as required by the `Channel` interface. + +- SHOULD restrict external apps from listening or publishing on this channel. +- 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. ### Broadcasting and listening for multiple context types -The [Context specification](../../context/spec#assumptions) recommends that complex context objects are defined using simpler context types for particular fields. For example, a `Position` is composed of an `Instrument` and a holding amount. This leads to situations where an application may be able to receive or respond to context objects that are embedded in a more complex type, but not the more complex type itself. For example, a pricing chart might respond to an `Instrument` but doesn't know how to handle a `Position`. -To facilitate context linking in such situations it is recommended that applications `broadcast` each context type that other apps (listening on a User Channel or App Channel) may wish to process, starting with the simpler types, followed by the complex type. Doing so allows applications to filter the context types they receive by adding listeners for specific context types - but requires that the application broadcasting context make multiple broadcast calls in quick succession when sharing its context. +The [Context specification](../../context/spec#assumptions) recommends that complex context objects are defined using simpler context types for particular fields. For example, a `Position` is composed of an `Instrument` and a holding amount. This leads to situations where an application may be able to receive or respond to context objects that are embedded in a more complex type, but not the more complex type itself. For example, a pricing chart might respond to an `Instrument` but doesn't know how to handle a `Position`. +To facilitate context linking in such situations it is recommended that applications `broadcast` each context type that other apps (listening on a User Channel or App Channel) may wish to process, starting with the simpler types, followed by the complex type. Doing so allows applications to filter the context types they receive by adding listeners for specific context types - but requires that the application broadcasting context make multiple broadcast calls in quick succession when sharing its context. From 413208b54cfcea193980e5209de6f7faba57717d Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 25 May 2022 12:32:22 +0100 Subject: [PATCH 09/23] tweakj recommended channel color schema again --- docs/api/spec.md | 6 +++--- src/api/RecommendedChannels.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index fd6728f59..b7bc56f17 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -410,7 +410,7 @@ const recommendedChannels = [ type: 'user', displayMetadata: { name: 'Channel 5', - color: 'turquoise', + color: 'cyan', glyph: '5', }, }, @@ -419,7 +419,7 @@ const recommendedChannels = [ type: 'user', displayMetadata: { name: 'Channel 6', - color: 'lightblue', + color: 'blue', glyph: '6', }, }, @@ -428,7 +428,7 @@ const recommendedChannels = [ type: 'user', displayMetadata: { name: 'Channel 7', - color: 'blue', + color: 'magenta', glyph: '7', }, }, diff --git a/src/api/RecommendedChannels.ts b/src/api/RecommendedChannels.ts index e5aab43e9..795bc9335 100644 --- a/src/api/RecommendedChannels.ts +++ b/src/api/RecommendedChannels.ts @@ -54,7 +54,7 @@ const recommendedChannels: Array = [ type: 'user', displayMetadata: { name: 'Channel 5', - color: 'turquoise', + color: 'cyan', glyph: '5', }, }, @@ -63,7 +63,7 @@ const recommendedChannels: Array = [ type: 'user', displayMetadata: { name: 'Channel 6', - color: 'lightblue', + color: 'blue', glyph: '6', }, }, @@ -72,7 +72,7 @@ const recommendedChannels: Array = [ type: 'user', displayMetadata: { name: 'Channel 7', - color: 'blue', + color: 'magenta', glyph: '7', }, }, From 55329685b37625d3865ed2a315dab3e321aeed88 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 10:09:00 +0100 Subject: [PATCH 10/23] Revert "move new elements to top-level of schema" This reverts commit aaad6d5c42efe12e77df1ef9190372f87721b7ce. --- src/app-directory/specification/appd.yaml | 141 ++++++++--------- .../static/schemas/next/app-directory.yaml | 145 ++++++++---------- 2 files changed, 122 insertions(+), 164 deletions(-) diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 559860f52..7c0c1fb8f 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -428,14 +428,8 @@ components: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - raiseIntent: - $ref: '#/components/schemas/RaiseIntent' - raiseIntentForContext: - $ref: '#/components/schemas/RaiseIntentForContext' - userChannels: - $ref: '#/components/schemas/UserChannels' - appChannels: - $ref: '#/components/schemas/AppChannels' + interopUse: + $ref: '#/components/schemas/InteropUse' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -549,13 +543,9 @@ components: $ref: '#/components/schemas/NameValuePair' intents: type: array - description: >- - The list of intents that the Application listens for and resolves as defined by + description: > + The list of intents implemented by the Application as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - Primarily used to implement intent resolution in a Desktop Agent, but may also be used, - for example in an app catalog UI, to find apps that 'interoperate with' other apps. - An Application MUST use the Desktop Agent's `addIntentListener` API to add an intent listener - for each intent listed here as soon as possible after it starts up. items: $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: @@ -795,81 +785,70 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - RaiseIntent: + InteropUse: type: object description: >- - Optional metadata that describes which Intents are raised by the application and context types they are - raised with. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise intents that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - RaiseIntentForContext: - type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow the user to - select an intent from those that are available via the Desktop Agent. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise intents that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - additionalProperties: - items: - type: string - UserChannels: - type: object - description: >- - Describes the application's use of context types on User Channels. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise contexts that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. + Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, + which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, + to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application + use to other app developers and desktop assemblers. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API + use MAY vary from what is described. properties: - broadcast: + raisesIntents: + type: object + description: Intents that are raised by the application and contexts they are raised with. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + raisesContexts: type: array - description: Context types that are broadcast by the application + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow + the user to select an intent from those that are available via the Desktop Agent. items: type: string - listen: + userChannels: + type: object + description: Describes the application's use of context types on User Channels + properties: + broadcast: + type: array + description: Context types that are broadcast by the application + items: + type: string + listen: + type: array + description: Context types that the application listens for + items: + type: string + appChannels: type: array - description: Context types that the application listens for + description: Describes the application's use of App Channels items: - type: string - AppChannels: - type: array - description: >- - Describes the application's use of App Channels. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise App channels and contexts that the application creates and uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - items: - type: object - required: - - name - properties: - name: - type: string - description: The name of the App Channel - description: - type: string - description: A description of how the channel is used - broadcast: - type: array - description: Context types that are broadcast by the application on the channel - items: - type: string - listen: - type: array - description: Context types that the application listens for on the channel - items: - type: string + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index 559860f52..db3ff3060 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -428,14 +428,8 @@ components: $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - raiseIntent: - $ref: '#/components/schemas/RaiseIntent' - raiseIntentForContext: - $ref: '#/components/schemas/RaiseIntentForContext' - userChannels: - $ref: '#/components/schemas/UserChannels' - appChannels: - $ref: '#/components/schemas/AppChannels' + interopUse: + $ref: '#/components/schemas/InteropUse' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -549,13 +543,9 @@ components: $ref: '#/components/schemas/NameValuePair' intents: type: array - description: >- - The list of intents that the Application listens for and resolves as defined by + description: > + The list of intents implemented by the Application as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - Primarily used to implement intent resolution in a Desktop Agent, but may also be used, - for example in an app catalog UI, to find apps that 'interoperate with' other apps. - An Application MUST use the Desktop Agent's `addIntentListener` API to add an intent listener - for each intent listed here as soon as possible after it starts up. items: $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: @@ -653,7 +643,7 @@ components: type: string description: >- A comma separated list of the types of contexts the intent offered by the application can process, - where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" + where the first part of the context type is the namespace e.g."fdc3.contact", "org.symphony.contact" resultType: type: string description: >- @@ -714,7 +704,7 @@ components: LaunchDetails: description: >- The type specific launch details of the application. These details are intended to be - vendor-agnostic and MAY be duplicated or overridden by details provided in the hostManifests + vendor-agnostic and _MAY_ be duplicated or overridden by details provided in the hostManifests object for a specific host. oneOf: - $ref: '#/components/schemas/WebAppDetails' @@ -795,81 +785,70 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - RaiseIntent: + InteropUse: type: object description: >- - Optional metadata that describes which Intents are raised by the application and context types they are - raised with. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise intents that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - RaiseIntentForContext: - type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow the user to - select an intent from those that are available via the Desktop Agent. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise intents that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - additionalProperties: - items: - type: string - UserChannels: - type: object - description: >- - Describes the application's use of context types on User Channels. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise contexts that the application uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. + Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, + which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, + to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application + use to other app developers and desktop assemblers. + The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API + use MAY vary from what is described. properties: - broadcast: + raisesIntents: + type: object + description: Intents that are raised by the application and contexts they are raised with. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context types that the intent may be raised with + items: + type: string + raisesContexts: type: array - description: Context types that are broadcast by the application + description: >- + Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow + the user to select an intent from those that are available via the Desktop Agent. items: type: string - listen: + userChannels: + type: object + description: Describes the application's use of context types on User Channels + properties: + broadcast: + type: array + description: Context types that are broadcast by the application + items: + type: string + listen: + type: array + description: Context types that the application listens for + items: + type: string + appChannels: type: array - description: Context types that the application listens for + description: Describes the application's use of App Channels items: - type: string - AppChannels: - type: array - description: >- - Describes the application's use of App Channels. - This metadata may be used, for example in an app catalog UI, to find apps that 'interoperate with' other - apps or to advertise App channels and contexts that the application creates and uses. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual usage - MAY vary from what is described. - items: - type: object - required: - - name - properties: - name: - type: string - description: The name of the App Channel - description: - type: string - description: A description of how the channel is used - broadcast: - type: array - description: Context types that are broadcast by the application on the channel - items: - type: string - listen: - type: array - description: Context types that the application listens for on the channel - items: - type: string + type: object + required: + - name + properties: + name: + type: string + description: The name of the App Channel + description: + type: string + description: A description of how the channel is used + broadcast: + type: array + description: Context types that are broadcast by the application on the channel + items: + type: string + listen: + type: array + description: Context types that the application listens for on the channel + items: + type: string examples: FDC3WorkbenchAppDefinition: value: From b087ebda47c13caf6302ddf26db9c5f6571cdcca Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 12:13:36 +0100 Subject: [PATCH 11/23] Updating to use interopMeta proposal C and to create an example --- src/app-directory/specification/appd.yaml | 709 +++++++++++------ .../static/schemas/next/app-directory.yaml | 711 ++++++++++++------ 2 files changed, 945 insertions(+), 475 deletions(-) diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 7c0c1fb8f..7dab32c42 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -29,6 +29,8 @@ paths: schema: $ref: '#/components/schemas/Application' examples: + MyAppDefinition: + $ref: '#/components/examples/MyAppDefinition' FDC3WorkbenchAppDefinition: $ref: '#/components/examples/FDC3WorkbenchAppDefinition' '400': @@ -64,8 +66,8 @@ paths: schema: $ref: '#/components/schemas/AllApplicationsResponse' examples: - FDC3WorkbenchAppDefinitionAllAppsResponse: - $ref: '#/components/examples/FDC3WorkbenchAppDefinitionAllAppsResponse' + AllAppsResponse: + $ref: '#/components/examples/AllAppsResponse' '400': description: Bad request. content: @@ -420,16 +422,10 @@ components: custom data from an App Directory to a launcher. items: $ref: '#/components/schemas/NameValuePair' - intents: - type: array - description: > - The list of intents that the Application listens for via `fdc3.addIntentListener()` - items: - $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - interopUse: - $ref: '#/components/schemas/InteropUse' + interopMeta: + $ref: '#/components/schemas/InteropMeta' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -449,105 +445,6 @@ components: properties: localizedVersions: $ref: '#/components/schemas/LocalizedVersions' - ApplicationV1: - description: > - (Deprecated v1 API version) Defines an application retrieved from an FDC3 App Directory, which can - then be launched. - Launching typically means running for a user on a desktop. - The details around 'launching' including who or what might do it, and how the launch action is initiated are - discussed elsewhere in the FDC3 App Directory spec. - required: - - appId - - name - - manifest - - manifestType - properties: - appId: - type: string - description: > - The unique application identifier located within a specific - application directory instance. - name: - type: string - description: > - The name of the application. - The name should be unique within an FDC3 App Directory instance. The - exception to the uniqueness constraint is that an App Directory can - hold definitions for multiple versions of the same app. - The same appName could occur in other directories. We are not - currently specifying app name conventions in the document. - manifest: - type: string - description: > - URI or full JSON of the application manifest providing all details related to launch - and use requirements as described by the vendor. - The format of this manifest is vendor specific, but can be identified by - the manifestType attribute. - manifestType: - type: string - description: > - The manifest type which relates to the format and structure of the manifest content. - The definition is based on the vendor specific format and definition outside of this specification. - version: - type: string - description: >- - Version of the application. This allows multiple app versions to be - defined using the same app name. This can be a triplet but can also - include things like 1.2.5 (BETA) - title: - type: string - description: >- - Optional title for the application, if missing use appName, - typically used in a launcher UI. - tooltip: - type: string - description: Optional tooltip description e.g. for a launcher - description: - type: string - description: >- - Description of the application. This will typically be a 1-2 - paragraph style blurb about the application. Allow mark up language - images: - type: array - description: >- - Array of images to show the user when they are looking at app - description. Each image can have an optional description/tooltip - items: - $ref: '#/components/schemas/AppImageV1' - contactEmail: - type: string - format: email - description: Optional e-mail to receive queries about the application - supportEmail: - type: string - format: email - description: Optional e-mail to receive support requests for the application - publisher: - type: string - description: >- - The name of the company that owns the application. The publisher has - control over their namespace/app/signature. - icons: - type: array - description: >- - Holds Icons used for the application, a Launcher may be able to use - multiple Icon sizes or there may be a 'button' Icon - items: - $ref: '#/components/schemas/IconV1' - customConfig: - type: array - description: >- - An optional set of name value pairs that can be used to deliver - custom data from an App Directory to a launcher. - items: - $ref: '#/components/schemas/NameValuePair' - intents: - type: array - description: > - The list of intents implemented by the Application as defined by - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - items: - $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: properties: applications: @@ -560,18 +457,6 @@ components: type: string description: | Response message providing status of query - ApplicationSearchResponseV1: - properties: - applications: - type: array - description: | - List of applications - items: - $ref: '#/components/schemas/ApplicationV1' - message: - type: string - description: | - Response message providing status of query NameValuePair: description: Simple name value pair properties: @@ -594,13 +479,6 @@ components: type: type: string description: Image media type. If not present the Desktop Agent may use the src file extension - IconV1: - description: (Deprecated v1 API version) Icon holder - properties: - icon: - type: string - format: uri - description: Icon URL Screenshot: description: Images representing the app in common usage scenarios properties: @@ -617,70 +495,6 @@ components: label: type: string description: Optional caption for the image - AppImageV1: - description: App Image holder - properties: - url: - type: string - format: uri - description: App Image URL - Intent: - description: >- - An intent definition as defined by spec - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - required: - - name - properties: - name: - type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. - displayName: - type: string - description: An optional display name for the intent that may be used in UI instead of the name. - contexts: - type: array - items: - type: string - description: >- - A comma separated list of the types of contexts the intent offered by the application can process, - where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" - resultType: - type: string - description: >- - An optional type for output returned by the application, if any, when resolving this intent. - May indicate a context type by type name (e.g. "fdc3.instrument"), a channel (e.g. "channel") - or a combination that indicates a channel that returns a particular context type - (e.g. "channel"). - customConfig: - type: object - description: >- - Custom configuration for the intent that may be required for a - particular desktop agent. - IntentV1: - description: >- - (Deprecated v1 API version) An intent definition as defined by spec - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - required: - - name - properties: - name: - type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. - displayName: - type: string - description: An optional display name for the intent that may be used in UI instead of the name. - contexts: - type: array - items: - type: string - description: >- - A comma sepaarted list of the types of contexts the intent offered by the application can process. - where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" - customConfig: - type: object - description: >- - Custom configuration for the intent that may be required for a - particular desktop agent. Type: type: string description: | @@ -785,49 +599,105 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - InteropUse: - type: object + Intent: description: >- - Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, - which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, - to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application - use to other app developers and desktop assemblers. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API - use MAY vary from what is described. + Definition of an intent that an app listens for + required: + - contexts properties: - raisesIntents: - type: object - description: Intents that are raised by the application and contexts they are raised with. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - raisesContexts: + displayName: + type: string + description: An optional display name for the intent that may be used in UI instead of the name. + contexts: type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow - the user to select an intent from those that are available via the Desktop Agent. items: type: string + description: >- + A comma separated list of the types of contexts the intent offered by the application can process, + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" + resultType: + type: string + description: >- + An optional type for output returned by the application, if any, when resolving this intent. + May indicate a context type by type name (e.g. "fdc3.instrument"), a channel (e.g. "channel") + or a combination that indicates a channel that returns a particular context type + (e.g. "channel"). + customConfig: + type: object + description: >- + Custom configuration for the intent that may be required for a + particular desktop agent. + InteropMeta: + type: object + description: | + Metadata that describes how the application uses FDC3 APIs. This metadata serves multiple purposes: + + - It supports intent resolution by a desktop agent, by declaring what intents an app listens for. + - It may be used, for example in an app catalog UI, to find apps that 'interoperate with' other apps. + - It provides a standard location to document how the app interacts with user channels, app channels, + and intents, for use by other app developers and desktop assemblers. + properties: + intents: + type: object + description: Describes the app's interactions with intents. + properties: + listensFor: + type: object + description: | + A mapping of Intents names that an app listens for via `fdc3.addIntentListener()` to their + configuration. + + Used to support intent resolution by desktop agents. + additionalProperties: + x-additionalPropertiesName: Intent name + items: + $ref: '#/components/schemas/Intent' + raises: + type: object + description: | + A mapping of Intent names that an app raises (via `fdc3.raiseIntent`) to an array of context + type names that it may be raised with. + + Use the intent name "any" to represent use of the `fdc3.raiseIntentForContext` and + `fdc3.findIntentForContext` functions, which allow the user to select from intents available for a + specified context type. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context type names that the intent may be raised with. + items: + type: string userChannels: type: object - description: Describes the application's use of context types on User Channels + description: | + Describes the application's use of context types on User Channels. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. properties: - broadcast: + broadcasts: type: array - description: Context types that are broadcast by the application + description: Context type names that are broadcast by the application. items: type: string - listen: + listensFor: type: array - description: Context types that the application listens for + description: Context type names that the application listens for. items: type: string appChannels: type: array - description: Describes the application's use of App Channels + description: | + Describes the application's use of App Channels. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. items: type: object required: @@ -835,20 +705,170 @@ components: properties: name: type: string - description: The name of the App Channel + description: The name of the App Channel. description: type: string - description: A description of how the channel is used - broadcast: + description: A description of how the channel is used. + broadcasts: type: array - description: Context types that are broadcast by the application on the channel + description: Context type names that are broadcast by the application on the channel. items: type: string - listen: + listensFor: type: array - description: Context types that the application listens for on the channel + description: Context type names that the application listens for on the channel. items: type: string + AppImageV1: + description: App Image holder + properties: + url: + type: string + format: uri + description: App Image URL + IconV1: + description: (Deprecated v1 API version) Icon holder + properties: + icon: + type: string + format: uri + description: Icon URL + IntentV1: + description: >- + (Deprecated v1 API version) An intent definition as defined by spec + https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + required: + - name + properties: + name: + type: string + description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. + displayName: + type: string + description: An optional display name for the intent that may be used in UI instead of the name. + contexts: + type: array + items: + type: string + description: >- + A comma sepaarted list of the types of contexts the intent offered by the application can process. + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" + customConfig: + type: object + description: >- + Custom configuration for the intent that may be required for a + particular desktop agent. + ApplicationV1: + description: > + (Deprecated v1 API version) Defines an application retrieved from an FDC3 App Directory, which can + then be launched. + Launching typically means running for a user on a desktop. + The details around 'launching' including who or what might do it, and how the launch action is initiated are + discussed elsewhere in the FDC3 App Directory spec. + required: + - appId + - name + - manifest + - manifestType + properties: + appId: + type: string + description: > + The unique application identifier located within a specific + application directory instance. + name: + type: string + description: > + The name of the application. + The name should be unique within an FDC3 App Directory instance. The + exception to the uniqueness constraint is that an App Directory can + hold definitions for multiple versions of the same app. + The same appName could occur in other directories. We are not + currently specifying app name conventions in the document. + manifest: + type: string + description: > + URI or full JSON of the application manifest providing all details related to launch + and use requirements as described by the vendor. + The format of this manifest is vendor specific, but can be identified by + the manifestType attribute. + manifestType: + type: string + description: > + The manifest type which relates to the format and structure of the manifest content. + The definition is based on the vendor specific format and definition outside of this specification. + version: + type: string + description: >- + Version of the application. This allows multiple app versions to be + defined using the same app name. This can be a triplet but can also + include things like 1.2.5 (BETA) + title: + type: string + description: >- + Optional title for the application, if missing use appName, + typically used in a launcher UI. + tooltip: + type: string + description: Optional tooltip description e.g. for a launcher + description: + type: string + description: >- + Description of the application. This will typically be a 1-2 + paragraph style blurb about the application. Allow mark up language + images: + type: array + description: >- + Array of images to show the user when they are looking at app + description. Each image can have an optional description/tooltip + items: + $ref: '#/components/schemas/AppImageV1' + contactEmail: + type: string + format: email + description: Optional e-mail to receive queries about the application + supportEmail: + type: string + format: email + description: Optional e-mail to receive support requests for the application + publisher: + type: string + description: >- + The name of the company that owns the application. The publisher has + control over their namespace/app/signature. + icons: + type: array + description: >- + Holds Icons used for the application, a Launcher may be able to use + multiple Icon sizes or there may be a 'button' Icon + items: + $ref: '#/components/schemas/IconV1' + customConfig: + type: array + description: >- + An optional set of name value pairs that can be used to deliver + custom data from an App Directory to a launcher. + items: + $ref: '#/components/schemas/NameValuePair' + intents: + type: array + description: > + The list of intents implemented by the Application as defined by + https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + items: + $ref: '#/components/schemas/IntentV1' + ApplicationSearchResponseV1: + properties: + applications: + type: array + description: | + List of applications + items: + $ref: '#/components/schemas/ApplicationV1' + message: + type: string + description: | + Response message providing status of query examples: FDC3WorkbenchAppDefinition: value: @@ -869,11 +889,6 @@ components: supportEmail: fdc3-maintainers@finos.org moreInfo: https://fdc3.finos.org #update to point to implementations page when it exists publisher: FDC3 - intents: [{ - name: ViewChart, - displayName: View Chart, - contexts: [fdc3.instrument] - }] type: browser details: url: https://fdc3.finos.org/toolbox/fdc3-workbench/ @@ -934,9 +949,234 @@ components: } } summary: A sample app definition for the FDC3 Workbench application - FDC3WorkbenchAppDefinitionAllAppsResponse: + MyAppDefinition: + value: + appId: my-application + name: my-application + title: My Application + description: An example application that uses FDC3 and fully describes itself in an AppD record. + categories: [market data, research, news] + version: 1.0.0 + tooltip: My example application definition + lang: en-US + icons: + - src: http://example.domain.com/assets/my-app-icon.png + size: 256x256 + type: image/png + screenshots: + - src: http://example.domain.com/assets/my-app-screenshot-1.png + label: The first screenshot of my example app + type: image/png + size: 800x600 + - src: http://example.domain.com/assets/my-app-screenshot-2.png + label: The second screenshot of my example app + type: image/png + size: 800x600 + contactEmail: fdc3@finos.org + supportEmail: fdc3-maintainers@finos.org + moreInfo: http://example.domain.com/ + publisher: Example App, Inc. + type: browser + details: + url: http://example.domain.com/app.html + hostManifests: { + Finsemble: { + window: { + left: 120, + top: 120, + width: 600, + height: 800, + options: { + minWidth: 75 + } + }, + foreign: { + components: { + App Launcher: { + launchableByUser: true + }, + Window Manager: { + FSBLHeader: true, + persistWindowState: true + } + } + }, + interop: { + autoConnect: true + } + }, + Glue42: { + type: window, + details: { + height: 800, + width: 600, + left: 120, + top: 120, + mode: tab, + allowChannels: true, + loader: { + enabled: true, + hideOnLoad: true + } + }, + customProperties: { + folder: FDC3 Toolbox + } + }, + Web App Manifest: http://example.domain.com/my-app.json + } + interopMeta: + intents: + listensFor: + ViewChart: + displayName: View Chart + contexts: + - fdc3.instrument + myApp.GetPrice: + displayName: Get Price + contexts: + - fdc3.instrument + resultType: myApp.quote + raises: + ViewOrders: + - fdc3.instrument + - fdc3.organization + StartEmail: + - fdc3.email + userChannels: + broadcasts: + - fdc3.instrument + - fdc3.organization + listensFor: + - fdc3.instrument + - fdc3.organization + appChannels: + - name: myApp.quotes, + description: >- + Used to share a stream of quotes for currently displayed instrument and may be used to change the currently displayed symbol, + broadcasts: + - myApp.quote + listensFor: + - fdc3.instrument + localizedVersions: + fr-FR: + title: Mon application, + description: Un exemple d'application qui utilise FDC3 et se décrit entièrement dans un enregistrement AppD. + summary: A sample app definition for an application that describes its use of interop. + AllAppsResponse: value: applications: # you can't $ref inside a $ref so example is repeated here for search response + - appId: my-application + name: my-application + title: My Application + description: An example application that uses FDC3 and fully describes itself in an AppD record. + categories: [market data, research, news] + version: 1.0.0 + tooltip: My example application definition + lang: en-US + icons: + - src: http://example.domain.com/assets/my-app-icon.png + size: 256x256 + type: image/png + screenshots: + - src: http://example.domain.com/assets/my-app-screenshot-1.png + label: The first screenshot of my example app + type: image/png + size: 800x600 + - src: http://example.domain.com/assets/my-app-screenshot-2.png + label: The second screenshot of my example app + type: image/png + size: 800x600 + contactEmail: fdc3@finos.org + supportEmail: fdc3-maintainers@finos.org + moreInfo: http://example.domain.com/ + publisher: Example App, Inc. + type: browser + details: + url: http://example.domain.com/app.html + hostManifests: { + Finsemble: { + window: { + left: 120, + top: 120, + width: 600, + height: 800, + options: { + minWidth: 75 + } + }, + foreign: { + components: { + App Launcher: { + launchableByUser: true + }, + Window Manager: { + FSBLHeader: true, + persistWindowState: true + } + } + }, + interop: { + autoConnect: true + } + }, + Glue42: { + type: window, + details: { + height: 800, + width: 600, + left: 120, + top: 120, + mode: tab, + allowChannels: true, + loader: { + enabled: true, + hideOnLoad: true + } + }, + customProperties: { + folder: FDC3 Toolbox + } + }, + Web App Manifest: http://example.domain.com/my-app.json + } + interopMeta: + intents: + listensFor: + ViewChart: + displayName: View Chart + contexts: + - fdc3.instrument + myApp.GetPrice: + displayName: Get Price + contexts: + - fdc3.instrument + resultType: myApp.quote + raises: + ViewOrders: + - fdc3.instrument + - fdc3.organization + StartEmail: + - fdc3.email + userChannels: + broadcasts: + - fdc3.instrument + - fdc3.organization + listensFor: + - fdc3.instrument + - fdc3.organization + appChannels: + - name: myApp.quotes, + description: >- + Used to share a stream of quotes for currently displayed instrument and may be used to change the currently displayed symbol, + broadcasts: + - myApp.quote + listensFor: + - fdc3.instrument + localizedVersions: + fr-FR: + title: Mon application, + description: Un exemple d'application qui utilise FDC3 et se décrit entièrement dans un enregistrement AppD. - appId: fdc3-workbench name: fdc3-workbench title: FDC3 Workbench @@ -953,11 +1193,6 @@ components: contactEmail: fdc3@finos.org, supportEmail: fdc3-maintainers@finos.org, publisher: FDC3, - intents: [{ - name: ViewChart, - displayName: View Chart, - contexts: [fdc3.instrument] - }] type: browser details: url: https://fdc3.finos.org/toolbox/fdc3-workbench/ @@ -1018,4 +1253,4 @@ components: } } message: OK - summary: A sample 'all applications' listing response containing the FDC3 Workbench application + summary: A sample 'all applications' listing response diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index db3ff3060..7dab32c42 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -29,6 +29,8 @@ paths: schema: $ref: '#/components/schemas/Application' examples: + MyAppDefinition: + $ref: '#/components/examples/MyAppDefinition' FDC3WorkbenchAppDefinition: $ref: '#/components/examples/FDC3WorkbenchAppDefinition' '400': @@ -64,8 +66,8 @@ paths: schema: $ref: '#/components/schemas/AllApplicationsResponse' examples: - FDC3WorkbenchAppDefinitionAllAppsResponse: - $ref: '#/components/examples/FDC3WorkbenchAppDefinitionAllAppsResponse' + AllAppsResponse: + $ref: '#/components/examples/AllAppsResponse' '400': description: Bad request. content: @@ -420,16 +422,10 @@ components: custom data from an App Directory to a launcher. items: $ref: '#/components/schemas/NameValuePair' - intents: - type: array - description: > - The list of intents that the Application listens for via `fdc3.addIntentListener()` - items: - $ref: '#/components/schemas/Intent' hostManifests: $ref: '#/components/schemas/HostManifests' - interopUse: - $ref: '#/components/schemas/InteropUse' + interopMeta: + $ref: '#/components/schemas/InteropMeta' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -449,105 +445,6 @@ components: properties: localizedVersions: $ref: '#/components/schemas/LocalizedVersions' - ApplicationV1: - description: > - (Deprecated v1 API version) Defines an application retrieved from an FDC3 App Directory, which can - then be launched. - Launching typically means running for a user on a desktop. - The details around 'launching' including who or what might do it, and how the launch action is initiated are - discussed elsewhere in the FDC3 App Directory spec. - required: - - appId - - name - - manifest - - manifestType - properties: - appId: - type: string - description: > - The unique application identifier located within a specific - application directory instance. - name: - type: string - description: > - The name of the application. - The name should be unique within an FDC3 App Directory instance. The - exception to the uniqueness constraint is that an App Directory can - hold definitions for multiple versions of the same app. - The same appName could occur in other directories. We are not - currently specifying app name conventions in the document. - manifest: - type: string - description: > - URI or full JSON of the application manifest providing all details related to launch - and use requirements as described by the vendor. - The format of this manifest is vendor specific, but can be identified by - the manifestType attribute. - manifestType: - type: string - description: > - The manifest type which relates to the format and structure of the manifest content. - The definition is based on the vendor specific format and definition outside of this specification. - version: - type: string - description: >- - Version of the application. This allows multiple app versions to be - defined using the same app name. This can be a triplet but can also - include things like 1.2.5 (BETA) - title: - type: string - description: >- - Optional title for the application, if missing use appName, - typically used in a launcher UI. - tooltip: - type: string - description: Optional tooltip description e.g. for a launcher - description: - type: string - description: >- - Description of the application. This will typically be a 1-2 - paragraph style blurb about the application. Allow mark up language - images: - type: array - description: >- - Array of images to show the user when they are looking at app - description. Each image can have an optional description/tooltip - items: - $ref: '#/components/schemas/AppImageV1' - contactEmail: - type: string - format: email - description: Optional e-mail to receive queries about the application - supportEmail: - type: string - format: email - description: Optional e-mail to receive support requests for the application - publisher: - type: string - description: >- - The name of the company that owns the application. The publisher has - control over their namespace/app/signature. - icons: - type: array - description: >- - Holds Icons used for the application, a Launcher may be able to use - multiple Icon sizes or there may be a 'button' Icon - items: - $ref: '#/components/schemas/IconV1' - customConfig: - type: array - description: >- - An optional set of name value pairs that can be used to deliver - custom data from an App Directory to a launcher. - items: - $ref: '#/components/schemas/NameValuePair' - intents: - type: array - description: > - The list of intents implemented by the Application as defined by - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - items: - $ref: '#/components/schemas/IntentV1' AllApplicationsResponse: properties: applications: @@ -560,18 +457,6 @@ components: type: string description: | Response message providing status of query - ApplicationSearchResponseV1: - properties: - applications: - type: array - description: | - List of applications - items: - $ref: '#/components/schemas/ApplicationV1' - message: - type: string - description: | - Response message providing status of query NameValuePair: description: Simple name value pair properties: @@ -594,13 +479,6 @@ components: type: type: string description: Image media type. If not present the Desktop Agent may use the src file extension - IconV1: - description: (Deprecated v1 API version) Icon holder - properties: - icon: - type: string - format: uri - description: Icon URL Screenshot: description: Images representing the app in common usage scenarios properties: @@ -617,70 +495,6 @@ components: label: type: string description: Optional caption for the image - AppImageV1: - description: App Image holder - properties: - url: - type: string - format: uri - description: App Image URL - Intent: - description: >- - An intent definition as defined by spec - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - required: - - name - properties: - name: - type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. - displayName: - type: string - description: An optional display name for the intent that may be used in UI instead of the name. - contexts: - type: array - items: - type: string - description: >- - A comma separated list of the types of contexts the intent offered by the application can process, - where the first part of the context type is the namespace e.g."fdc3.contact", "org.symphony.contact" - resultType: - type: string - description: >- - An optional type for output returned by the application, if any, when resolving this intent. - May indicate a context type by type name (e.g. "fdc3.instrument"), a channel (e.g. "channel") - or a combination that indicates a channel that returns a particular context type - (e.g. "channel"). - customConfig: - type: object - description: >- - Custom configuration for the intent that may be required for a - particular desktop agent. - IntentV1: - description: >- - (Deprecated v1 API version) An intent definition as defined by spec - https://github.com/FDC3/Intents/blob/master/src/Intent.yaml - required: - - name - properties: - name: - type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. - displayName: - type: string - description: An optional display name for the intent that may be used in UI instead of the name. - contexts: - type: array - items: - type: string - description: >- - A comma sepaarted list of the types of contexts the intent offered by the application can process. - where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" - customConfig: - type: object - description: >- - Custom configuration for the intent that may be required for a - particular desktop agent. Type: type: string description: | @@ -704,7 +518,7 @@ components: LaunchDetails: description: >- The type specific launch details of the application. These details are intended to be - vendor-agnostic and _MAY_ be duplicated or overridden by details provided in the hostManifests + vendor-agnostic and MAY be duplicated or overridden by details provided in the hostManifests object for a specific host. oneOf: - $ref: '#/components/schemas/WebAppDetails' @@ -785,49 +599,105 @@ components: additionalProperties: x-additionalPropertiesName: Language tag $ref: '#/components/schemas/BaseApplication' # due to a bug in redoc this may display as a recursive definition, it is not. It will render correctly in swagger and other OpenAPI parsers. - InteropUse: - type: object + Intent: description: >- - Optional metadata that describes how the application uses FDC3 APIs (beyond intents it listens for, - which are defined under the `intents` element). This metadata may be used, for example in an app catalog UI, - to find apps that 'interoperate with' other apps or to advertise app channels or intents that the application - use to other app developers and desktop assemblers. - The presence of this metadata does not imply any form of contract with the DesktopAgent and actual FDC3 API - use MAY vary from what is described. + Definition of an intent that an app listens for + required: + - contexts properties: - raisesIntents: - type: object - description: Intents that are raised by the application and contexts they are raised with. - additionalProperties: - x-additionalPropertiesName: Intent name - type: array - description: Context types that the intent may be raised with - items: - type: string - raisesContexts: + displayName: + type: string + description: An optional display name for the intent that may be used in UI instead of the name. + contexts: type: array - description: >- - Contexts that are raised via `raiseIntentForContext` or `findIntentForContext`, which allow - the user to select an intent from those that are available via the Desktop Agent. items: type: string + description: >- + A comma separated list of the types of contexts the intent offered by the application can process, + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" + resultType: + type: string + description: >- + An optional type for output returned by the application, if any, when resolving this intent. + May indicate a context type by type name (e.g. "fdc3.instrument"), a channel (e.g. "channel") + or a combination that indicates a channel that returns a particular context type + (e.g. "channel"). + customConfig: + type: object + description: >- + Custom configuration for the intent that may be required for a + particular desktop agent. + InteropMeta: + type: object + description: | + Metadata that describes how the application uses FDC3 APIs. This metadata serves multiple purposes: + + - It supports intent resolution by a desktop agent, by declaring what intents an app listens for. + - It may be used, for example in an app catalog UI, to find apps that 'interoperate with' other apps. + - It provides a standard location to document how the app interacts with user channels, app channels, + and intents, for use by other app developers and desktop assemblers. + properties: + intents: + type: object + description: Describes the app's interactions with intents. + properties: + listensFor: + type: object + description: | + A mapping of Intents names that an app listens for via `fdc3.addIntentListener()` to their + configuration. + + Used to support intent resolution by desktop agents. + additionalProperties: + x-additionalPropertiesName: Intent name + items: + $ref: '#/components/schemas/Intent' + raises: + type: object + description: | + A mapping of Intent names that an app raises (via `fdc3.raiseIntent`) to an array of context + type names that it may be raised with. + + Use the intent name "any" to represent use of the `fdc3.raiseIntentForContext` and + `fdc3.findIntentForContext` functions, which allow the user to select from intents available for a + specified context type. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. + additionalProperties: + x-additionalPropertiesName: Intent name + type: array + description: Context type names that the intent may be raised with. + items: + type: string userChannels: type: object - description: Describes the application's use of context types on User Channels + description: | + Describes the application's use of context types on User Channels. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. properties: - broadcast: + broadcasts: type: array - description: Context types that are broadcast by the application + description: Context type names that are broadcast by the application. items: type: string - listen: + listensFor: type: array - description: Context types that the application listens for + description: Context type names that the application listens for. items: type: string appChannels: type: array - description: Describes the application's use of App Channels + description: | + Describes the application's use of App Channels. + + This metadata is not currently used by the desktop agent, but is provided to help find apps + that will interoperate with this app and to document API interactions for use by other app + developers. items: type: object required: @@ -835,20 +705,170 @@ components: properties: name: type: string - description: The name of the App Channel + description: The name of the App Channel. description: type: string - description: A description of how the channel is used - broadcast: + description: A description of how the channel is used. + broadcasts: type: array - description: Context types that are broadcast by the application on the channel + description: Context type names that are broadcast by the application on the channel. items: type: string - listen: + listensFor: type: array - description: Context types that the application listens for on the channel + description: Context type names that the application listens for on the channel. items: type: string + AppImageV1: + description: App Image holder + properties: + url: + type: string + format: uri + description: App Image URL + IconV1: + description: (Deprecated v1 API version) Icon holder + properties: + icon: + type: string + format: uri + description: Icon URL + IntentV1: + description: >- + (Deprecated v1 API version) An intent definition as defined by spec + https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + required: + - name + properties: + name: + type: string + description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. + displayName: + type: string + description: An optional display name for the intent that may be used in UI instead of the name. + contexts: + type: array + items: + type: string + description: >- + A comma sepaarted list of the types of contexts the intent offered by the application can process. + where the first part of the context type is the namespace e.g."fdc3.contact, org.symphony.contact" + customConfig: + type: object + description: >- + Custom configuration for the intent that may be required for a + particular desktop agent. + ApplicationV1: + description: > + (Deprecated v1 API version) Defines an application retrieved from an FDC3 App Directory, which can + then be launched. + Launching typically means running for a user on a desktop. + The details around 'launching' including who or what might do it, and how the launch action is initiated are + discussed elsewhere in the FDC3 App Directory spec. + required: + - appId + - name + - manifest + - manifestType + properties: + appId: + type: string + description: > + The unique application identifier located within a specific + application directory instance. + name: + type: string + description: > + The name of the application. + The name should be unique within an FDC3 App Directory instance. The + exception to the uniqueness constraint is that an App Directory can + hold definitions for multiple versions of the same app. + The same appName could occur in other directories. We are not + currently specifying app name conventions in the document. + manifest: + type: string + description: > + URI or full JSON of the application manifest providing all details related to launch + and use requirements as described by the vendor. + The format of this manifest is vendor specific, but can be identified by + the manifestType attribute. + manifestType: + type: string + description: > + The manifest type which relates to the format and structure of the manifest content. + The definition is based on the vendor specific format and definition outside of this specification. + version: + type: string + description: >- + Version of the application. This allows multiple app versions to be + defined using the same app name. This can be a triplet but can also + include things like 1.2.5 (BETA) + title: + type: string + description: >- + Optional title for the application, if missing use appName, + typically used in a launcher UI. + tooltip: + type: string + description: Optional tooltip description e.g. for a launcher + description: + type: string + description: >- + Description of the application. This will typically be a 1-2 + paragraph style blurb about the application. Allow mark up language + images: + type: array + description: >- + Array of images to show the user when they are looking at app + description. Each image can have an optional description/tooltip + items: + $ref: '#/components/schemas/AppImageV1' + contactEmail: + type: string + format: email + description: Optional e-mail to receive queries about the application + supportEmail: + type: string + format: email + description: Optional e-mail to receive support requests for the application + publisher: + type: string + description: >- + The name of the company that owns the application. The publisher has + control over their namespace/app/signature. + icons: + type: array + description: >- + Holds Icons used for the application, a Launcher may be able to use + multiple Icon sizes or there may be a 'button' Icon + items: + $ref: '#/components/schemas/IconV1' + customConfig: + type: array + description: >- + An optional set of name value pairs that can be used to deliver + custom data from an App Directory to a launcher. + items: + $ref: '#/components/schemas/NameValuePair' + intents: + type: array + description: > + The list of intents implemented by the Application as defined by + https://github.com/FDC3/Intents/blob/master/src/Intent.yaml + items: + $ref: '#/components/schemas/IntentV1' + ApplicationSearchResponseV1: + properties: + applications: + type: array + description: | + List of applications + items: + $ref: '#/components/schemas/ApplicationV1' + message: + type: string + description: | + Response message providing status of query examples: FDC3WorkbenchAppDefinition: value: @@ -869,11 +889,6 @@ components: supportEmail: fdc3-maintainers@finos.org moreInfo: https://fdc3.finos.org #update to point to implementations page when it exists publisher: FDC3 - intents: [{ - name: ViewChart, - displayName: View Chart, - contexts: [fdc3.instrument] - }] type: browser details: url: https://fdc3.finos.org/toolbox/fdc3-workbench/ @@ -934,9 +949,234 @@ components: } } summary: A sample app definition for the FDC3 Workbench application - FDC3WorkbenchAppDefinitionAllAppsResponse: + MyAppDefinition: + value: + appId: my-application + name: my-application + title: My Application + description: An example application that uses FDC3 and fully describes itself in an AppD record. + categories: [market data, research, news] + version: 1.0.0 + tooltip: My example application definition + lang: en-US + icons: + - src: http://example.domain.com/assets/my-app-icon.png + size: 256x256 + type: image/png + screenshots: + - src: http://example.domain.com/assets/my-app-screenshot-1.png + label: The first screenshot of my example app + type: image/png + size: 800x600 + - src: http://example.domain.com/assets/my-app-screenshot-2.png + label: The second screenshot of my example app + type: image/png + size: 800x600 + contactEmail: fdc3@finos.org + supportEmail: fdc3-maintainers@finos.org + moreInfo: http://example.domain.com/ + publisher: Example App, Inc. + type: browser + details: + url: http://example.domain.com/app.html + hostManifests: { + Finsemble: { + window: { + left: 120, + top: 120, + width: 600, + height: 800, + options: { + minWidth: 75 + } + }, + foreign: { + components: { + App Launcher: { + launchableByUser: true + }, + Window Manager: { + FSBLHeader: true, + persistWindowState: true + } + } + }, + interop: { + autoConnect: true + } + }, + Glue42: { + type: window, + details: { + height: 800, + width: 600, + left: 120, + top: 120, + mode: tab, + allowChannels: true, + loader: { + enabled: true, + hideOnLoad: true + } + }, + customProperties: { + folder: FDC3 Toolbox + } + }, + Web App Manifest: http://example.domain.com/my-app.json + } + interopMeta: + intents: + listensFor: + ViewChart: + displayName: View Chart + contexts: + - fdc3.instrument + myApp.GetPrice: + displayName: Get Price + contexts: + - fdc3.instrument + resultType: myApp.quote + raises: + ViewOrders: + - fdc3.instrument + - fdc3.organization + StartEmail: + - fdc3.email + userChannels: + broadcasts: + - fdc3.instrument + - fdc3.organization + listensFor: + - fdc3.instrument + - fdc3.organization + appChannels: + - name: myApp.quotes, + description: >- + Used to share a stream of quotes for currently displayed instrument and may be used to change the currently displayed symbol, + broadcasts: + - myApp.quote + listensFor: + - fdc3.instrument + localizedVersions: + fr-FR: + title: Mon application, + description: Un exemple d'application qui utilise FDC3 et se décrit entièrement dans un enregistrement AppD. + summary: A sample app definition for an application that describes its use of interop. + AllAppsResponse: value: applications: # you can't $ref inside a $ref so example is repeated here for search response + - appId: my-application + name: my-application + title: My Application + description: An example application that uses FDC3 and fully describes itself in an AppD record. + categories: [market data, research, news] + version: 1.0.0 + tooltip: My example application definition + lang: en-US + icons: + - src: http://example.domain.com/assets/my-app-icon.png + size: 256x256 + type: image/png + screenshots: + - src: http://example.domain.com/assets/my-app-screenshot-1.png + label: The first screenshot of my example app + type: image/png + size: 800x600 + - src: http://example.domain.com/assets/my-app-screenshot-2.png + label: The second screenshot of my example app + type: image/png + size: 800x600 + contactEmail: fdc3@finos.org + supportEmail: fdc3-maintainers@finos.org + moreInfo: http://example.domain.com/ + publisher: Example App, Inc. + type: browser + details: + url: http://example.domain.com/app.html + hostManifests: { + Finsemble: { + window: { + left: 120, + top: 120, + width: 600, + height: 800, + options: { + minWidth: 75 + } + }, + foreign: { + components: { + App Launcher: { + launchableByUser: true + }, + Window Manager: { + FSBLHeader: true, + persistWindowState: true + } + } + }, + interop: { + autoConnect: true + } + }, + Glue42: { + type: window, + details: { + height: 800, + width: 600, + left: 120, + top: 120, + mode: tab, + allowChannels: true, + loader: { + enabled: true, + hideOnLoad: true + } + }, + customProperties: { + folder: FDC3 Toolbox + } + }, + Web App Manifest: http://example.domain.com/my-app.json + } + interopMeta: + intents: + listensFor: + ViewChart: + displayName: View Chart + contexts: + - fdc3.instrument + myApp.GetPrice: + displayName: Get Price + contexts: + - fdc3.instrument + resultType: myApp.quote + raises: + ViewOrders: + - fdc3.instrument + - fdc3.organization + StartEmail: + - fdc3.email + userChannels: + broadcasts: + - fdc3.instrument + - fdc3.organization + listensFor: + - fdc3.instrument + - fdc3.organization + appChannels: + - name: myApp.quotes, + description: >- + Used to share a stream of quotes for currently displayed instrument and may be used to change the currently displayed symbol, + broadcasts: + - myApp.quote + listensFor: + - fdc3.instrument + localizedVersions: + fr-FR: + title: Mon application, + description: Un exemple d'application qui utilise FDC3 et se décrit entièrement dans un enregistrement AppD. - appId: fdc3-workbench name: fdc3-workbench title: FDC3 Workbench @@ -953,11 +1193,6 @@ components: contactEmail: fdc3@finos.org, supportEmail: fdc3-maintainers@finos.org, publisher: FDC3, - intents: [{ - name: ViewChart, - displayName: View Chart, - contexts: [fdc3.instrument] - }] type: browser details: url: https://fdc3.finos.org/toolbox/fdc3-workbench/ @@ -1018,4 +1253,4 @@ components: } } message: OK - summary: A sample 'all applications' listing response containing the FDC3 Workbench application + summary: A sample 'all applications' listing response From 27843482a006e7a01cb8d79768fd3238f8e27c33 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 12:24:14 +0100 Subject: [PATCH 12/23] fix capitalisation of application in descriptions --- src/app-directory/specification/appd.yaml | 4 ++-- website/static/schemas/next/app-directory.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 1aac5fefe..74b8c230b 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -742,7 +742,7 @@ components: properties: name: type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. + description: The name of the intent to 'launch'. In this case the name of an Intent supported by an application. displayName: type: string description: An optional display name for the intent that may be used in UI instead of the name. @@ -853,7 +853,7 @@ components: intents: type: array description: > - The list of intents implemented by the Application as defined by + The list of intents implemented by the application as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml items: $ref: '#/components/schemas/IntentV1' diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index 1aac5fefe..74b8c230b 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -742,7 +742,7 @@ components: properties: name: type: string - description: The name of the intent to 'launch'. In this case the name of an Intent supported by an Application. + description: The name of the intent to 'launch'. In this case the name of an Intent supported by an application. displayName: type: string description: An optional display name for the intent that may be used in UI instead of the name. @@ -853,7 +853,7 @@ components: intents: type: array description: > - The list of intents implemented by the Application as defined by + The list of intents implemented by the application as defined by https://github.com/FDC3/Intents/blob/master/src/Intent.yaml items: $ref: '#/components/schemas/IntentV1' From b7c6b5f4331f49ba70cde794e26fc749b8c55607 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 12:27:49 +0100 Subject: [PATCH 13/23] markdown lint appD overview --- docs/app-directory/overview.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/app-directory/overview.md b/docs/app-directory/overview.md index 39feefd3e..564aae7a2 100644 --- a/docs/app-directory/overview.md +++ b/docs/app-directory/overview.md @@ -36,7 +36,7 @@ Typically, software evolves over time. The app versions you are running today wi #### Agent-agnostic -As a part of the FDC3 standard, appD isn't tied to any specific vendor. This is important, as it allows you more flexibility in that you are not tied to any specific container or desktop agent implementation. If at any point you want to switch to a different desktop agent, the process won’t be prohibitively difficult. The existing appD will work without any additional effort from you, as long as your new desktop agent is also FDC3-compliant. This is in contrast with proprietary solutions, where you would have to produce a new configuration and integration for every application. +As a part of the FDC3 standard, appD isn't tied to any specific vendor. This is important, as it allows you more flexibility in that you are not tied to any specific container or desktop agent implementation. If at any point you want to switch to a different desktop agent, the process won’t be prohibitively difficult. The existing appD will work without any additional effort from you, as long as your new desktop agent is also FDC3-compliant. This is in contrast with proprietary solutions, where you would have to produce a new configuration and integration for every application. AppD reduces fragmentation in the market and allows end-users more flexibility in what applications can be included in their desktop. @@ -62,7 +62,7 @@ By hosting our own appD we can easily combine applications from various provider ## Relationship to Other Standards -The App Directory's application record is similar to application manifests defined in other standards, in particular the W3C's [Web Application Manifest](https://www.w3.org/TR/appmanifest/). However, the App Directory, and by extension the application record, serve a different set of use-cases specific to application interoperability on financial services desktops, which other standards do not fully address. +The App Directory's application record is similar to application manifests defined in other standards, in particular the W3C's [Web Application Manifest](https://www.w3.org/TR/appmanifest/). However, the App Directory, and by extension the application record, serve a different set of use-cases specific to application interoperability on financial services desktops, which other standards do not fully address. Wherever possible, FDC3 seeks to draw inspiration from, align itself with and reference other standards - ensuring that conventions and best practices developed by those standards are reused, along with the standard itself (e.g. data formats in ISO standard formats, external links to technology-specific manifest file formats etc.). For a list of standards that FDC3 references, see the [References](../references) page. @@ -107,7 +107,7 @@ Although the concept of fully qualified application IDs are useful in resolving ## Service Discovery -In order to support the discovery of applications that can be used with a desktop agent, it is necessary to access data stored in one or more app directory instances. +In order to support the discovery of applications that can be used with a desktop agent, it is necessary to access data stored in one or more app directory instances. ![img](assets/appd_service_distribution.png) @@ -115,7 +115,7 @@ However, in order to do so, you must first discover the location of an app direc Three methods for discovering app directory services are defined in this Standard: - 1. **Static configuration:** Statically defined URI records for use within client applications (typically a desktop agent implementation) directly. + 1. **Static configuration:** Statically defined URI records for use within client applications (typically a desktop agent implementation) directly. 2. **Fully-qualified appID namespace syntax host resolution:** Discovery of the appD location using a fully qualified application ID (appId) domain name. 3. **DNS lookup by domain name:** Discovery of the appD location using a domain name to lookup DNS SRV records identifying the host server location and TCP port. ([RFC2782](https://tools.ietf.org/html/rfc2782)) @@ -134,14 +134,12 @@ An app directory URI can be constructed using a [fully qualified application ID] A launcher can then easily construct a URI by: 1. URI protocol is defaulted to `https`, but can be overridden by the launcher. -2. URI hostname is the fully qualified domain of the application ID. +2. URI hostname is the fully qualified domain of the application ID. 3. URI port is default `https/443`, but can be overridden by the launcher 4. URI url is by default `"/api/appd/(version)"` . Calls that are made without version MUST automatically default to latest, i.e. `"/api/appd/app1"` should return the same result as `"/api/appd/v2/app1"`. The resulting URI to retrieve application data for "app1" would be "[https://appd.foo.com/api/appd/v2/app1@appd.foo.com](https://appd.foo.com/api/appd/v2/app1@appd.foo.com)" - - ### DNS/SRV Records Another approach to support app directory service discovery (resolution) is through use of existing domain name service (DNS) implementations that are broadly used on the Internet today (see: [RFCs](https://www.isc.org/community/rfcs/dns/)). Name service implementations can be considered critical infrastructure and are proven stable with over twenty years of use. Name services can be used both through public Internet or locally deployed intranet, which provides optionality to deployment schemes. @@ -159,8 +157,8 @@ zone name { _service._proto.name. TTL class SRV priority weight port target.} - *class*: standard DNS class field (this is always *IN*). - *priority*: the priority of the target host, lower value means more preferred. - *weight*: A relative weight for records with the same priority, higher value means more preferred. -- *port*: the TCP or UDP port on which the service is to be found. For AppD service, TCP should always be used. -- *target*: the canonical hostname of the machine providing the service, ending in a dot. This would be the host where the AppD service is running. +- *port*: the TCP or UDP port on which the service is to be found. For AppD service, TCP should always be used. +- *target*: the canonical hostname of the machine providing the service, ending in a dot. This would be the host where the AppD service is running. For AppD Service the SRV record MUST use the following definitions: From 9298f36f0b6f09f4efa728b81fcc2b203ce46e95 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 12:51:17 +0100 Subject: [PATCH 14/23] namespacing recommended channel names --- docs/api/spec.md | 16 ++++++++-------- src/api/RecommendedChannels.ts | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/api/spec.md b/docs/api/spec.md index b7bc56f17..f5c5ed39c 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -370,7 +370,7 @@ Desktop Agent implementations SHOULD use the following set of channels, to enabl ```javascript const recommendedChannels = [ { - id: 'Channel 1', + id: 'fdc3.channel.1', type: 'user', displayMetadata: { name: 'Channel 1', @@ -379,7 +379,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 2', + id: 'fdc3.channel.2', type: 'user', displayMetadata: { name: 'Channel 2', @@ -388,7 +388,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 3', + id: 'fdc3.channel.3', type: 'user', displayMetadata: { name: 'Channel 3', @@ -397,7 +397,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 4', + id: 'fdc3.channel.4', type: 'user', displayMetadata: { name: 'Channel 4', @@ -406,7 +406,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 5', + id: 'fdc3.channel.5', type: 'user', displayMetadata: { name: 'Channel 5', @@ -415,7 +415,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 6', + id: 'fdc3.channel.6', type: 'user', displayMetadata: { name: 'Channel 6', @@ -424,7 +424,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 7', + id: 'fdc3.channel.7', type: 'user', displayMetadata: { name: 'Channel 7', @@ -433,7 +433,7 @@ const recommendedChannels = [ }, }, { - id: 'Channel 8', + id: 'fdc3.channel.8', type: 'user', displayMetadata: { name: 'Channel 8', diff --git a/src/api/RecommendedChannels.ts b/src/api/RecommendedChannels.ts index 795bc9335..502181a85 100644 --- a/src/api/RecommendedChannels.ts +++ b/src/api/RecommendedChannels.ts @@ -14,7 +14,7 @@ interface UserChannelTemplate { const recommendedChannels: Array = [ { - id: 'Channel 1', + id: 'fdc3.channel.1', type: 'user', displayMetadata: { name: 'Channel 1', @@ -23,7 +23,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 2', + id: 'fdc3.channel.2', type: 'user', displayMetadata: { name: 'Channel 2', @@ -32,7 +32,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 3', + id: 'fdc3.channel.3', type: 'user', displayMetadata: { name: 'Channel 3', @@ -41,7 +41,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 4', + id: 'fdc3.channel.4', type: 'user', displayMetadata: { name: 'Channel 4', @@ -50,7 +50,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 5', + id: 'fdc3.channel.5', type: 'user', displayMetadata: { name: 'Channel 5', @@ -59,7 +59,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 6', + id: 'fdc3.channel.6', type: 'user', displayMetadata: { name: 'Channel 6', @@ -68,7 +68,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 7', + id: 'fdc3.channel.7', type: 'user', displayMetadata: { name: 'Channel 7', @@ -77,7 +77,7 @@ const recommendedChannels: Array = [ }, }, { - id: 'Channel 8', + id: 'fdc3.channel.8', type: 'user', displayMetadata: { name: 'Channel 8', From 7299630d9c121eb074ffb55bf7e8bb6f9c778e09 Mon Sep 17 00:00:00 2001 From: Kris West Date: Thu, 26 May 2022 12:57:45 +0100 Subject: [PATCH 15/23] whitespace change to trigger deploy --- website/blog/2017-09-25-testing-rss.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/blog/2017-09-25-testing-rss.md b/website/blog/2017-09-25-testing-rss.md index b7ff8129c..401995913 100644 --- a/website/blog/2017-09-25-testing-rss.md +++ b/website/blog/2017-09-25-testing-rss.md @@ -9,3 +9,4 @@ authorFBID: 661277173 This should be truncated. This line should never render in XML. + \ No newline at end of file From 49eac4b1566239d5809caffe3068f04d23835e37 Mon Sep 17 00:00:00 2001 From: Kris West Date: Fri, 27 May 2022 11:58:47 +0100 Subject: [PATCH 16/23] Improve readability of API Errors, Metadata and Types definitions by limiting line lengths to 80 chars and adding a blank line between definitions --- docs/api/ref/Errors.md | 61 +++++++++++++++++++++------- docs/api/ref/Metadata.md | 85 +++++++++++++++++++++++++++------------- docs/api/ref/Types.md | 11 ++++-- 3 files changed, 113 insertions(+), 44 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index ad30be91a..de351d78d 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -10,11 +10,16 @@ Some FDC3 API operations return promises that can result in errors. enum OpenError { /** Returned if the specified application is not found.*/ AppNotFound = 'AppNotFound', + /** Returned if the specified application fails to launch correctly.*/ ErrorOnLaunch = 'ErrorOnLaunch', - /** Returned if the specified application launches but fails to add a context listener in order to receive the context passed to the `fdc3.open` call.*/ + + /** Returned if the specified application launches but fails to add a context + * listener in order to receive the context passed to the `fdc3.open` call.*/ AppTimeout = 'AppTimeout', - /** Returned if the FDC3 desktop agent implementation is not currently able to handle the request.*/ + + /** Returned if the FDC3 desktop agent implementation is not currently able + * to handle the request.*/ ResolverUnavailable = 'ResolverUnavailable', } ``` @@ -22,25 +27,41 @@ enum OpenError { Contains constants representing the errors that can be encountered when calling the [`open`](DesktopAgent#open) method on the [DesktopAgent](DesktopAgent) object. #### See also + * [`DesktopAgent.open`](DesktopAgent#open) ## `ResolveError` ```typescript export enum ResolveError { - /** SHOULD be returned if no apps are available that can resolve the intent and context combination.*/ + /** SHOULD be returned if no apps are available that can resolve the intent + * and context combination.*/ NoAppsFound = 'NoAppsFound', - /** Returned if the FDC3 desktop agent implementation is not currently able to handle the request.*/ + + /** Returned if the FDC3 desktop agent implementation is not currently able + * to handle the request.*/ ResolverUnavailable = 'ResolverUnavailable', - /** Returned if the user cancelled the resolution request, for example by closing or cancelling a resolver UI.*/ + + /** Returned if the user cancelled the resolution request, for example by + * closing or cancelling a resolver UI.*/ UserCancelled = 'UserCancelledResolution', - /** SHOULD be returned if a timeout cancels an intent resolution that required user interaction. Please use `ResolverUnavailable` instead for situations where a resolver UI or similar fails.*/ + + /** SHOULD be returned if a timeout cancels an intent resolution that + * required user interaction. Please use `ResolverUnavailable` instead for + * situations where a resolver UI or similar fails.*/ ResolverTimeout = 'ResolverTimeout', - /** Returned if a specified target application is not available or a new instance of it cannot be opened. */ + + /** Returned if a specified target application is not available or a new + * instance of it cannot be opened. */ TargetAppUnavailable = 'TargetAppUnavailable', - /** Returned if a specified target application instance is not available, for example because it has been closed. */ + + /** Returned if a specified target application instance is not available, + * for example because it has been closed. */ TargetInstanceUnavailable = 'TargetInstanceUnavailable', - /** Returned if the intent and context could not be delivered to the selected application or instance, for example because it has not added an intent handler within a timeout.*/ + + /** Returned if the intent and context could not be delivered to the selected + * application or instance, for example because it has not added an intent + * handler within a timeout.*/ IntentDeliveryFailed = 'IntentDeliveryFailed', } ``` @@ -48,6 +69,7 @@ export enum ResolveError { Contains constants representing the errors that can be encountered when calling the [`findIntent`](DesktopAgent#findintent), [`findIntentsByContext`](DesktopAgent#findintentsbycontext), [`raiseIntent`](DesktopAgent#raiseintent) or [`raiseIntentForContext`](DesktopAgent#raiseintentforcontext) methods on the [DesktopAgent](DesktopAgent). #### See also + * [`DesktopAgent.findIntent`](DesktopAgent#findintent) * [`DesktopAgent.findIntentsByContext`](DesktopAgent#findintentsbycontext) * [`DesktopAgent.raiseIntent`](DesktopAgent#raiseintent) @@ -57,9 +79,12 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ResultError { - /** Returned if the intent handler exited without returning a Promise or that Promise was not resolved with a Context or Channel object. */ + /** Returned if the intent handler exited without returning a Promise or that + * Promise was not resolved with a Context or Channel object. */ NoResultReturned = 'NoResultReturned', - /** Returned if the Intent handler function processing the raised intent throws an error or rejects the Promise it returned. */ + + /** Returned if the Intent handler function processing the raised intent + * throws an error or rejects the Promise it returned. */ IntentHandlerRejected = 'IntentHandlerRejected', } ``` @@ -67,6 +92,7 @@ enum ResultError { Contains constants representing the errors that can be encountered when calling the [`getResult`](DesktopAgent#findintent) method on the [IntentResolution](Metadata#intentresolution) Object. #### See also + * [`DesktopAgent.addIntentListener`](DesktopAgent#addintentlistener) * [`DesktopAgent.raiseIntent`](DesktopAgent#raiseintent) * [`IntentResolution`](Metadata#intentresolution) @@ -75,11 +101,17 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ChannelError { - /** Returned if the specified channel is not found when attempting to join a channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ + /** Returned if the specified channel is not found when attempting to join a + * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ NoChannelFound = 'NoChannelFound', - /** SHOULD be returned when a request to join a user channel or to a retrieve a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods of the DesktopAgent (`fdc3`) object is denied. */ + + /** SHOULD be returned when a request to join a user channel or to a retrieve + * a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods + * of the DesktopAgent (`fdc3`) object is denied. */ AccessDenied = 'AccessDenied', - /** SHOULD be returned when a channel cannot be created or retrieved via the `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ + + /** SHOULD be returned when a channel cannot be created or retrieved via the + * `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ CreationFailed = 'CreationFailed', } ``` @@ -87,6 +119,7 @@ enum ChannelError { Contains constants representing the errors that can be encountered when calling channels using the [`joinUserChannel`](DesktopAgent#joinuserchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](Channel#getcurrentcontext), [`broadcast`](Channel#broadcast) or [`addContextListener`](Channel#addcontextlistener) methods on the `Channel` object. #### See also + * [`DesktopAgent.joinUserChannel`](DesktopAgent#joinuserchannel) * [`DesktopAgent.getOrCreateChannel`](DesktopAgent#getorcreatechannel) * [`Channel.broadcast`](Channel#broadcast) diff --git a/docs/api/ref/Metadata.md b/docs/api/ref/Metadata.md index 384597a45..2687f8839 100644 --- a/docs/api/ref/Metadata.md +++ b/docs/api/ref/Metadata.md @@ -8,8 +8,11 @@ FDC3 API operations return various types of metadata. ```ts interface AppIntent { - /** Details of the intent whose relationship to resolving applications is being described. */ + /** Details of the intent whose relationship to resolving applications is + * being described. + */ readonly intent: IntentMetadata; + /** Details of applications that can resolve the intent. */ readonly apps: Array; } @@ -30,37 +33,52 @@ For each intent, it reference the applications that support that intent. ```ts interface AppMetadata extends AppIdentifier { /** - The 'friendly' app name. - This field was used with the `open` and `raiseIntent` calls in FDC3 <2.0, which now require an `AppIdentifier` wth `appId` set. - Note that for display purposes the `title` field should be used, if set, in preference to this field. + The 'friendly' app name. This field was used with the `open` and + `raiseIntent` calls in FDC3 <2.0, which now require an `AppIdentifier` + with `appId` set. + + Note that for display purposes the `title` field should be used, if set, + in preference to this field. */ readonly name?: string; /** The version of the application. */ readonly version?: string; - /** An optional set of, implementation specific, metadata fields that can be used to disambiguate instances, such as a window title or screen position. Must only be set if `instanceId` is set. */ + /** An optional set of, implementation specific, metadata fields that can be + * used to disambiguate instances, such as a window title or screen position. + * Must only be set if `instanceId` is set. + */ readonly instanceMetadata?: Record; - /** A more user-friendly application title that can be used to render UI elements */ + /** A more user-friendly application title that can be used to render UI + * elements. + */ readonly title?: string; - /** A tooltip for the application that can be used to render UI elements */ + /** A tooltip for the application that can be used to render UI elements. */ readonly tooltip?: string; - /** A longer, multi-paragraph description for the application that could include mark-up */ + /** A longer, multi-paragraph description for the application that could + * include mark-up. + */ readonly description?: string; - /** A list of icon URLs for the application that can be used to render UI elements */ + /** A list of icon URLs for the application that can be used to render UI + * elements. + */ readonly icons?: Array; - /** A list of image URLs for the application that can be used to render UI elements */ + /** A list of image URLs for the application that can be used to render UI + * elements. + */ readonly images?: Array; /** The type of result returned for any intent specified during resolution. * May express a particular context type (e.g. "fdc3.instrument"), channel * (e.g. "channel") or a channel that will receive a specified type - * (e.g. "channel"). */ + * (e.g. "channel"). + */ readonly resultType?: string | null; } ``` @@ -85,16 +103,17 @@ Note that as `AppMetadata` instances are also `AppIdentifiers` they may be passe ```ts interface DisplayMetadata { /** - * A user-readable name for this channel, e.g: `"Red"` - */ + * A user-readable name for this channel, e.g: `"Red"`. */ readonly name?: string; + /** - * The color that should be associated within this channel when displaying this channel in a UI, e.g: `#FF0000`. May be any color value supported by CSS, e.g. name, hex, rgba, etc.. - */ + * The color that should be associated within this channel when displaying + * this channel in a UI, e.g: `#FF0000`. May be any color value supported by + * CSS, e.g. name, hex, rgba, etc.. */ readonly color?: string; + /** - * A URL of an image that can be used to display this channel - */ + * A URL of an image that can be used to display this channel. */ readonly glyph?: string; } ``` @@ -157,13 +176,19 @@ The media type of the icon. If not provided the Desktop Agent may refer to the s ```ts interface ImplementationMetadata { - /** 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. + /** 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. */ readonly fdc3Version: string; - /** The name of the provider of the FDC3 Desktop Agent Implementation (e.g. Finsemble, Glue42, OpenFin etc.). */ + + /** The name of the provider of the FDC3 Desktop Agent Implementation + * (e.g.Finsemble, Glue42, OpenFin etc.). + */ readonly provider: string; - /** The version of the provider of the FDC3 Desktop Agent Implementation (e.g. 5.3.0). */ + + /** The version of the provider of the FDC3 Desktop Agent Implementation + * (e.g. 5.3.0). + */ readonly providerVersion?: string; } ``` @@ -178,16 +203,19 @@ Metadata relating to the FDC3 [DesktopAgent](DesktopAgent) object and its provid ```ts interface IntentMetadata { - /** The unique name of the intent that can be invoked by the raiseIntent call */ + /** The unique name of the intent that can be invoked by the raiseIntent call. + */ readonly name: string; - /** A friendly display name for the intent that should be used to render UI elements */ + + /** A friendly display name for the intent that should be used to render UI + * elements. + */ readonly displayName: string; } ``` The interface used to describe an intent within the platform. - #### See also * [`AppIntent.intent`](#appintent) @@ -198,20 +226,23 @@ The interface used to describe an intent within the platform. interface IntentResolution { /** - * Metadata about the app instance that was selected (or started) to resolve the intent. - * `source.instanceId` MUST be set, indicating the specific app instance that - * received the intent. + * Metadata about the app instance that was selected (or started) to resolve + * the intent. `source.instanceId` MUST be set, indicating the specific app + * instance that received the intent. */ readonly source: AppMetadata; + /** * The intent that was raised. May be used to determine which intent the user * chose in response to `fdc3.raiseIntentForContext()`. */ readonly intent: string; + /** * The version number of the Intents schema being used. */ readonly version?: string; + /** * Retrieves a promise that will resolve to either `Context` data returned * by the application that resolves the raised intent or a `Channel` diff --git a/docs/api/ref/Types.md b/docs/api/ref/Types.md index 7ceeb1844..444148675 100644 --- a/docs/api/ref/Types.md +++ b/docs/api/ref/Types.md @@ -12,9 +12,14 @@ If the `instanceId` field is set then the `AppMetadata` object represents a spec ```ts interface AppIdentifier { - /** The unique application identifier located within a specific application directory instance. An example of an appId might be 'app@sub.root' */ + /** The unique application identifier located within a specific application + * directory instance. An example of an appId might be 'app@sub.root'. + */ readonly appId: string; - /** An optional instance identifier, indicating that this object represents a specific instance of the application described.*/ + + /** An optional instance identifier, indicating that this object represents a + * specific instance of the application described. + */ readonly instanceId?: string; } ``` @@ -95,7 +100,7 @@ Used when attaching listeners for raised intents. type IntentResult = Context | Channel; ``` -Describes results that an Intent handler may optionally return that should be communicated back to the app that raised the intent, via the [`IntentResolution`](Metadata#intentresolution). +Describes results that an Intent handler may optionally 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`. From f270735845421ba3c2a9b4a476d397f88f50433f Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 30 May 2022 13:20:23 +0100 Subject: [PATCH 17/23] Correct wonky comment indentation --- docs/api/ref/Errors.md | 34 ++++++++++++------------ docs/api/ref/Metadata.md | 56 ++++++++++++++++++++-------------------- docs/api/ref/Types.md | 4 +-- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index de351d78d..5c3365177 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -15,11 +15,11 @@ enum OpenError { ErrorOnLaunch = 'ErrorOnLaunch', /** Returned if the specified application launches but fails to add a context - * listener in order to receive the context passed to the `fdc3.open` call.*/ + * listener in order to receive the context passed to the `fdc3.open` call.*/ AppTimeout = 'AppTimeout', /** Returned if the FDC3 desktop agent implementation is not currently able - * to handle the request.*/ + * to handle the request.*/ ResolverUnavailable = 'ResolverUnavailable', } ``` @@ -35,33 +35,33 @@ Contains constants representing the errors that can be encountered when calling ```typescript export enum ResolveError { /** SHOULD be returned if no apps are available that can resolve the intent - * and context combination.*/ + * and context combination.*/ NoAppsFound = 'NoAppsFound', /** Returned if the FDC3 desktop agent implementation is not currently able - * to handle the request.*/ + * to handle the request.*/ ResolverUnavailable = 'ResolverUnavailable', /** Returned if the user cancelled the resolution request, for example by - * closing or cancelling a resolver UI.*/ + * closing or cancelling a resolver UI.*/ UserCancelled = 'UserCancelledResolution', /** SHOULD be returned if a timeout cancels an intent resolution that - * required user interaction. Please use `ResolverUnavailable` instead for - * situations where a resolver UI or similar fails.*/ + * required user interaction. Please use `ResolverUnavailable` instead for + * situations where a resolver UI or similar fails.*/ ResolverTimeout = 'ResolverTimeout', /** Returned if a specified target application is not available or a new - * instance of it cannot be opened. */ + * instance of it cannot be opened. */ TargetAppUnavailable = 'TargetAppUnavailable', /** Returned if a specified target application instance is not available, - * for example because it has been closed. */ + * for example because it has been closed. */ TargetInstanceUnavailable = 'TargetInstanceUnavailable', /** Returned if the intent and context could not be delivered to the selected - * application or instance, for example because it has not added an intent - * handler within a timeout.*/ + * application or instance, for example because it has not added an intent + * handler within a timeout.*/ IntentDeliveryFailed = 'IntentDeliveryFailed', } ``` @@ -80,11 +80,11 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ResultError { /** Returned if the intent handler exited without returning a Promise or that - * Promise was not resolved with a Context or Channel object. */ + * Promise was not resolved with a Context or Channel object. */ NoResultReturned = 'NoResultReturned', /** Returned if the Intent handler function processing the raised intent - * throws an error or rejects the Promise it returned. */ + * throws an error or rejects the Promise it returned. */ IntentHandlerRejected = 'IntentHandlerRejected', } ``` @@ -102,16 +102,16 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ChannelError { /** Returned if the specified channel is not found when attempting to join a - * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ + * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ NoChannelFound = 'NoChannelFound', /** SHOULD be returned when a request to join a user channel or to a retrieve - * a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods - * of the DesktopAgent (`fdc3`) object is denied. */ + * a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods + * of the DesktopAgent (`fdc3`) object is denied. */ AccessDenied = 'AccessDenied', /** SHOULD be returned when a channel cannot be created or retrieved via the - * `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ + * `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ CreationFailed = 'CreationFailed', } ``` diff --git a/docs/api/ref/Metadata.md b/docs/api/ref/Metadata.md index 2687f8839..2efcadfc2 100644 --- a/docs/api/ref/Metadata.md +++ b/docs/api/ref/Metadata.md @@ -9,7 +9,7 @@ FDC3 API operations return various types of metadata. ```ts interface AppIntent { /** Details of the intent whose relationship to resolving applications is - * being described. + * being described. */ readonly intent: IntentMetadata; @@ -33,12 +33,13 @@ For each intent, it reference the applications that support that intent. ```ts interface AppMetadata extends AppIdentifier { /** - The 'friendly' app name. This field was used with the `open` and - `raiseIntent` calls in FDC3 <2.0, which now require an `AppIdentifier` - with `appId` set. - - Note that for display purposes the `title` field should be used, if set, - in preference to this field. + /** + * The 'friendly' app name. This field was used with the `open` and + * `raiseIntent` calls in FDC3 <2.0, which now require an `AppIdentifier` + * with `appId` set. + * + * Note that for display purposes the `title` field should be used, if set, + * in preference to this field. */ readonly name?: string; @@ -52,32 +53,32 @@ interface AppMetadata extends AppIdentifier { readonly instanceMetadata?: Record; /** A more user-friendly application title that can be used to render UI - * elements. + * elements. */ readonly title?: string; - /** A tooltip for the application that can be used to render UI elements. */ + /** A tooltip for the application that can be used to render UI elements. */ readonly tooltip?: string; /** A longer, multi-paragraph description for the application that could - * include mark-up. + * include mark-up. */ readonly description?: string; /** A list of icon URLs for the application that can be used to render UI - * elements. + * elements. */ readonly icons?: Array; /** A list of image URLs for the application that can be used to render UI - * elements. + * elements. */ readonly images?: Array; /** The type of result returned for any intent specified during resolution. - * May express a particular context type (e.g. "fdc3.instrument"), channel - * (e.g. "channel") or a channel that will receive a specified type - * (e.g. "channel"). + * May express a particular context type (e.g. "fdc3.instrument"), channel + * (e.g. "channel") or a channel that will receive a specified type + * (e.g. "channel"). */ readonly resultType?: string | null; } @@ -103,17 +104,17 @@ Note that as `AppMetadata` instances are also `AppIdentifiers` they may be passe ```ts interface DisplayMetadata { /** - * A user-readable name for this channel, e.g: `"Red"`. */ + * A user-readable name for this channel, e.g: `"Red"`. */ readonly name?: string; /** - * The color that should be associated within this channel when displaying - * this channel in a UI, e.g: `#FF0000`. May be any color value supported by - * CSS, e.g. name, hex, rgba, etc.. */ + * The color that should be associated within this channel when displaying + * this channel in a UI, e.g: `#FF0000`. May be any color value supported by + * CSS, e.g. name, hex, rgba, etc.. */ readonly color?: string; /** - * A URL of an image that can be used to display this channel. */ + * A URL of an image that can be used to display this channel. */ readonly glyph?: string; } ``` @@ -182,12 +183,12 @@ interface ImplementationMetadata { readonly fdc3Version: string; /** The name of the provider of the FDC3 Desktop Agent Implementation - * (e.g.Finsemble, Glue42, OpenFin etc.). + * (e.g.Finsemble, Glue42, OpenFin etc.). */ readonly provider: string; /** The version of the provider of the FDC3 Desktop Agent Implementation - * (e.g. 5.3.0). + * (e.g. 5.3.0). */ readonly providerVersion?: string; } @@ -203,12 +204,11 @@ Metadata relating to the FDC3 [DesktopAgent](DesktopAgent) object and its provid ```ts interface IntentMetadata { - /** The unique name of the intent that can be invoked by the raiseIntent call. - */ + /** The unique name of the intent that can be invoked by the raiseIntent call. */ readonly name: string; /** A friendly display name for the intent that should be used to render UI - * elements. + * elements. */ readonly displayName: string; } @@ -226,9 +226,9 @@ The interface used to describe an intent within the platform. interface IntentResolution { /** - * Metadata about the app instance that was selected (or started) to resolve - * the intent. `source.instanceId` MUST be set, indicating the specific app - * instance that received the intent. + * Metadata about the app instance that was selected (or started) to resolve + * the intent. `source.instanceId` MUST be set, indicating the specific app + * instance that received the intent. */ readonly source: AppMetadata; diff --git a/docs/api/ref/Types.md b/docs/api/ref/Types.md index 444148675..3335e4ac1 100644 --- a/docs/api/ref/Types.md +++ b/docs/api/ref/Types.md @@ -13,12 +13,12 @@ If the `instanceId` field is set then the `AppMetadata` object represents a spec ```ts interface AppIdentifier { /** The unique application identifier located within a specific application - * directory instance. An example of an appId might be 'app@sub.root'. + * directory instance. An example of an appId might be 'app@sub.root'. */ readonly appId: string; /** An optional instance identifier, indicating that this object represents a - * specific instance of the application described. + * specific instance of the application described. */ readonly instanceId?: string; } From dfe43c19ad61b31366fe937dd3c7b56f76baf327 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 30 May 2022 13:27:57 +0100 Subject: [PATCH 18/23] more comment cleanup --- docs/api/ref/Errors.md | 42 +++++++++++++++++++++++++------------- docs/api/ref/Metadata.md | 44 +++++++++++++++++++--------------------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/docs/api/ref/Errors.md b/docs/api/ref/Errors.md index 5c3365177..76ab4070e 100644 --- a/docs/api/ref/Errors.md +++ b/docs/api/ref/Errors.md @@ -15,11 +15,13 @@ enum OpenError { ErrorOnLaunch = 'ErrorOnLaunch', /** Returned if the specified application launches but fails to add a context - * listener in order to receive the context passed to the `fdc3.open` call.*/ + * listener in order to receive the context passed to the `fdc3.open` call. + */ AppTimeout = 'AppTimeout', /** Returned if the FDC3 desktop agent implementation is not currently able - * to handle the request.*/ + * to handle the request. + */ ResolverUnavailable = 'ResolverUnavailable', } ``` @@ -35,33 +37,40 @@ Contains constants representing the errors that can be encountered when calling ```typescript export enum ResolveError { /** SHOULD be returned if no apps are available that can resolve the intent - * and context combination.*/ + * and context combination. + */ NoAppsFound = 'NoAppsFound', /** Returned if the FDC3 desktop agent implementation is not currently able - * to handle the request.*/ + * to handle the request. + */ ResolverUnavailable = 'ResolverUnavailable', /** Returned if the user cancelled the resolution request, for example by - * closing or cancelling a resolver UI.*/ + * closing or cancelling a resolver UI. + */ UserCancelled = 'UserCancelledResolution', /** SHOULD be returned if a timeout cancels an intent resolution that * required user interaction. Please use `ResolverUnavailable` instead for - * situations where a resolver UI or similar fails.*/ + * situations where a resolver UI or similar fails. + */ ResolverTimeout = 'ResolverTimeout', /** Returned if a specified target application is not available or a new - * instance of it cannot be opened. */ + * instance of it cannot be opened. + */ TargetAppUnavailable = 'TargetAppUnavailable', /** Returned if a specified target application instance is not available, - * for example because it has been closed. */ + * for example because it has been closed. + */ TargetInstanceUnavailable = 'TargetInstanceUnavailable', /** Returned if the intent and context could not be delivered to the selected * application or instance, for example because it has not added an intent - * handler within a timeout.*/ + * handler within a timeout. + */ IntentDeliveryFailed = 'IntentDeliveryFailed', } ``` @@ -80,11 +89,13 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ResultError { /** Returned if the intent handler exited without returning a Promise or that - * Promise was not resolved with a Context or Channel object. */ + * Promise was not resolved with a Context or Channel object. + */ NoResultReturned = 'NoResultReturned', /** Returned if the Intent handler function processing the raised intent - * throws an error or rejects the Promise it returned. */ + * throws an error or rejects the Promise it returned. + */ IntentHandlerRejected = 'IntentHandlerRejected', } ``` @@ -102,16 +113,19 @@ Contains constants representing the errors that can be encountered when calling ```typescript enum ChannelError { /** Returned if the specified channel is not found when attempting to join a - * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`).*/ + * channel via the `joinUserChannel` function of the DesktopAgent (`fdc3`). + */ NoChannelFound = 'NoChannelFound', /** SHOULD be returned when a request to join a user channel or to a retrieve * a Channel object via the `joinUserChannel` or `getOrCreateChannel` methods - * of the DesktopAgent (`fdc3`) object is denied. */ + * of the DesktopAgent (`fdc3`) object is denied. + */ AccessDenied = 'AccessDenied', /** SHOULD be returned when a channel cannot be created or retrieved via the - * `getOrCreateChannel` method of the DesktopAgent (`fdc3`).*/ + * `getOrCreateChannel` method of the DesktopAgent (`fdc3`). + */ CreationFailed = 'CreationFailed', } ``` diff --git a/docs/api/ref/Metadata.md b/docs/api/ref/Metadata.md index 63e635278..b10414b95 100644 --- a/docs/api/ref/Metadata.md +++ b/docs/api/ref/Metadata.md @@ -192,7 +192,9 @@ interface ImplementationMetadata { */ readonly providerVersion?: string; - /** The calling application instance's own metadata, according to the Desktop Agent (MUST include at least the `appId` and `instanceId`). */ + /** The calling application instance's own metadata, according to the + * Desktop Agent (MUST include at least the `appId` and `instanceId`). + */ readonly appMetadata: AppMetadata; } ``` @@ -229,38 +231,34 @@ The interface used to describe an intent within the platform. ```ts interface IntentResolution { - /** - * Metadata about the app instance that was selected (or started) to resolve + /** Metadata about the app instance that was selected (or started) to resolve * the intent. `source.instanceId` MUST be set, indicating the specific app * instance that received the intent. */ readonly source: AppMetadata; - /** - * The intent that was raised. May be used to determine which intent the user - * chose in response to `fdc3.raiseIntentForContext()`. + /** The intent that was raised. May be used to determine which intent the user + * chose in response to `fdc3.raiseIntentForContext()`. */ readonly intent: string; - /** - * The version number of the Intents schema being used. + /** The version number of the Intents schema being used. */ readonly version?: string; - /** - * Retrieves a promise that will resolve to either `Context` data returned - * by the application that resolves the raised intent or a `Channel` - * established and returned by the app resolving the intent. + /** Retrieves a promise that will resolve to either `Context` data returned + * by the application that resolves the raised intent or a `Channel` + * established and returned by the app resolving the intent. * - * A `Channel` returned MAY be of the `PrivateChannel` type. The - * client can then `addContextListener()` on that channel to, for example, - * receive a stream of data. + * A `Channel` returned MAY be of the `PrivateChannel` type. The + * client can then `addContextListener()` on that channel to, for example, + * receive a stream of data. * - * If an error occurs (i.e. an error is thrown by the handler function, - * the promise it returns is rejected, or a promise is not returned by the - * handler function) then the Desktop Agent MUST reject the promise returned - * by the `getResult()` function of the `IntentResolution` with a string from - * the `ResultError` enumeration. + * If an error occurs (i.e. an error is thrown by the handler function, + * the promise it returns is rejected, or a promise is not returned by the + * handler function) then the Desktop Agent MUST reject the promise returned + * by the `getResult()` function of the `IntentResolution` with a string from + * the `ResultError` enumeration. */ getResult(): Promise; } @@ -271,7 +269,7 @@ IntentResolution provides a standard format for data returned upon resolving an #### Examples ```js -//resolve a "Chain" type intent +// Resolve a "Chain" type intent let resolution = await agent.raiseIntent("intentName", context); // Use metadata about the resolving app instance to target a further intent @@ -279,12 +277,12 @@ try { const resolution = await fdc3.raiseIntent('StageOrder', context); ... - //some time later + //Some time later await agent.raiseIntent("UpdateOrder", context, resolution.source); } catch (err) { ... } -//resolve a "Client-Service" type intent with a data or channel response +//Resolve a "Client-Service" type intent with a data or channel response let resolution = await agent.raiseIntent("intentName", context); try { const result = await resolution.getResult(); From cb47110a5edafe162661d3a8579b75935d51ac69 Mon Sep 17 00:00:00 2001 From: Kris West Date: Wed, 1 Jun 2022 17:37:26 +0100 Subject: [PATCH 19/23] fix broken changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b340d1c9..bf36849c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added the ability to return a Channel from an intent (via the `IntentResult` type), resolver support for intents that return Channels and the concept of PrivateChannels. ([#508](https://github.com/finos/FDC3/pull/508)) * Added error `UserCancelled` to the `ResolveError` enumeration to be used when user closes the resolver UI or otherwise cancels resolution of a raised intent ([#522](https://github.com/finos/FDC3/pull/522)) * Added `IntentDeliveryFailed` to the `ResolveError` enumeration to be used when delivery of an intent and context to a targetted app or instance fails. ([#601](https://github.com/finos/FDC3/pull/601)) -* Added an `instanceId` (and optional `instanceMetadata`) field to `AppMetadata` allowing it to refer to specific app instances and thereby supporting targetting of intents to specific app instances. Also added a `findInstances()` function to the desktop agent and `TargetAppUnavailable` and `TargetInstanceUnavailable` Errors to the `ResolveError` enumeration. ([#509]((https://github.com/finos/FDC3/pull/509)) +* Added an `instanceId` (and optional `instanceMetadata`) field to `AppMetadata` allowing it to refer to specific app instances and thereby supporting targetting of intents to specific app instances. Also added a `findInstances()` function to the desktop agent and `TargetAppUnavailable` and `TargetInstanceUnavailable` Errors to the `ResolveError` enumeration. ([#509](https://github.com/finos/FDC3/pull/509)) * Added a References and Bibliography section to the Standard's documentation to hold links to 'normative references' and other documentation that is useful for understanding the standard ([#530](https://github.com/finos/FDC3/pull/530)) * Added a Trademarks page to website to acknowledge trademarks used within the Standard not owned by FINOS or the Linux Foundation ([#534](https://github.com/finos/FDC3/pull/534)) * Added details of FDC3's existing versioning and deprecation policies to the FDC3 compliance page ([#539](https://github.com/finos/FDC3/pull/539)) From 74df3e854f9105e16bddff81f74710aada2d76af Mon Sep 17 00:00:00 2001 From: Juan Estrella Date: Wed, 1 Jun 2022 19:33:31 +0200 Subject: [PATCH 20/23] add symphony logo --- website/static/img/users/Symphony.png | Bin 0 -> 122149 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/static/img/users/Symphony.png diff --git a/website/static/img/users/Symphony.png b/website/static/img/users/Symphony.png new file mode 100644 index 0000000000000000000000000000000000000000..510f319f3ca38c20435b9ab310b9fa353fa28011 GIT binary patch literal 122149 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 OCn+i`QXr)J_WuF6f0RrB literal 0 HcmV?d00001 From 305c106d78b6d917c3b7ffdc0fa7baf86d875b99 Mon Sep 17 00:00:00 2001 From: Juan Estrella Date: Wed, 1 Jun 2022 19:39:01 +0200 Subject: [PATCH 21/23] add symphony to users data --- website/data/users.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/data/users.json b/website/data/users.json index 3f5eb806e..9aa6bc51b 100644 --- a/website/data/users.json +++ b/website/data/users.json @@ -95,5 +95,12 @@ "infoLink": "https://www.scottlogic.com/", "pinned": true, "isMember": true + }, + { + "caption": "Symphony", + "image": "/img/users/Symphony.png", + "infoLink": "https://www.symphony.com/", + "pinned": true, + "isMember": true } ] From 88ee333bc48a58e10159c3d203c2cd9ce6687857 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 22:28:42 +0000 Subject: [PATCH 22/23] Bump eventsource from 1.1.0 to 1.1.1 in /toolbox/fdc3-workbench Bumps [eventsource](https://github.com/EventSource/eventsource) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/EventSource/eventsource/releases) - [Changelog](https://github.com/EventSource/eventsource/blob/master/HISTORY.md) - [Commits](https://github.com/EventSource/eventsource/compare/v1.1.0...v1.1.1) --- updated-dependencies: - dependency-name: eventsource dependency-type: indirect ... Signed-off-by: dependabot[bot] --- toolbox/fdc3-workbench/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/toolbox/fdc3-workbench/yarn.lock b/toolbox/fdc3-workbench/yarn.lock index 153feb00a..55248a8ff 100644 --- a/toolbox/fdc3-workbench/yarn.lock +++ b/toolbox/fdc3-workbench/yarn.lock @@ -4913,9 +4913,9 @@ events@^3.0.0: integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== eventsource@^1.0.7: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.0.tgz#00e8ca7c92109e94b0ddf32dac677d841028cfaf" - integrity sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg== + version "1.1.1" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.1.1.tgz#4544a35a57d7120fba4fa4c86cb4023b2c09df2f" + integrity sha512-qV5ZC0h7jYIAOhArFJgSfdyz6rALJyb270714o7ZtNnw2WSJ+eexhKtE0O8LYPRsHZHf2osHKZBxGPvm3kPkCA== dependencies: original "^1.0.0" From acdca7414aff48dc71681976c94c72c35c0daf91 Mon Sep 17 00:00:00 2001 From: Kris West Date: Mon, 6 Jun 2022 16:03:18 +0100 Subject: [PATCH 23/23] rename interopMeta -> interop in appD schema --- CHANGELOG.md | 2 +- src/app-directory/specification/appd.yaml | 12 ++++++------ website/static/schemas/next/app-directory.yaml | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12bf9f778..61790a487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,7 +62,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * App Directory endpoint for searching applications was removed as searches over multiple app directories are better implemented by retrieving all the records and searching over the resulting combined dataset ([#696](https://github.com/finos/FDC3/pull/696)) * Extended Intent Naming conventions and added hyperlinks for existing Intent spec definitions ([#701](https://github.com/finos/FDC3/pull/701)) * Extended recommended field type conventions for contexts to include types for ids, times, dates, currency codes and country codes. The `fdc3.country` context type was updated to comply with the recommended field name for country codes (`COUNTRY_ISOALPHA2`). ([#704](https://github.com/finos/FDC3/pull/704)) -* The intents field of an AppD application records has been replaced with the `interop` field, to more fully describe an app's use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) +* The `intents` field of an AppD application records has been replaced with the `interop` field, to more fully describe an app's use of FDC3 and enable search for apps that 'interoperate' with a selected app ([#697](https://github.com/finos/FDC3/pull/697)) ### Deprecated diff --git a/src/app-directory/specification/appd.yaml b/src/app-directory/specification/appd.yaml index 74b8c230b..f647bca71 100644 --- a/src/app-directory/specification/appd.yaml +++ b/src/app-directory/specification/appd.yaml @@ -424,8 +424,8 @@ components: $ref: '#/components/schemas/NameValuePair' hostManifests: $ref: '#/components/schemas/HostManifests' - interopMeta: - $ref: '#/components/schemas/InteropMeta' + interop: + $ref: '#/components/schemas/Interop' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -627,7 +627,7 @@ components: description: >- Custom configuration for the intent that may be required for a particular desktop agent. - InteropMeta: + Interop: type: object description: | Metadata that describes how the application uses FDC3 APIs. This metadata serves multiple purposes: @@ -647,7 +647,7 @@ components: A mapping of Intents names that an app listens for via `fdc3.addIntentListener()` to their configuration. - Used to support intent resolution by desktop agents. + Used to support intent resolution by desktop agents. Replaces the `intents` element used in appD records prior to FDC3 2.0. additionalProperties: x-additionalPropertiesName: Intent name items: @@ -1025,7 +1025,7 @@ components: }, Web App Manifest: http://example.domain.com/my-app.json } - interopMeta: + interop: intents: listensFor: ViewChart: @@ -1140,7 +1140,7 @@ components: }, Web App Manifest: http://example.domain.com/my-app.json } - interopMeta: + interop: intents: listensFor: ViewChart: diff --git a/website/static/schemas/next/app-directory.yaml b/website/static/schemas/next/app-directory.yaml index 74b8c230b..f647bca71 100644 --- a/website/static/schemas/next/app-directory.yaml +++ b/website/static/schemas/next/app-directory.yaml @@ -424,8 +424,8 @@ components: $ref: '#/components/schemas/NameValuePair' hostManifests: $ref: '#/components/schemas/HostManifests' - interopMeta: - $ref: '#/components/schemas/InteropMeta' + interop: + $ref: '#/components/schemas/Interop' Application: description: > Defines an application retrieved from an FDC3 App Directory, which can @@ -627,7 +627,7 @@ components: description: >- Custom configuration for the intent that may be required for a particular desktop agent. - InteropMeta: + Interop: type: object description: | Metadata that describes how the application uses FDC3 APIs. This metadata serves multiple purposes: @@ -647,7 +647,7 @@ components: A mapping of Intents names that an app listens for via `fdc3.addIntentListener()` to their configuration. - Used to support intent resolution by desktop agents. + Used to support intent resolution by desktop agents. Replaces the `intents` element used in appD records prior to FDC3 2.0. additionalProperties: x-additionalPropertiesName: Intent name items: @@ -1025,7 +1025,7 @@ components: }, Web App Manifest: http://example.domain.com/my-app.json } - interopMeta: + interop: intents: listensFor: ViewChart: @@ -1140,7 +1140,7 @@ components: }, Web App Manifest: http://example.domain.com/my-app.json } - interopMeta: + interop: intents: listensFor: ViewChart: