diff --git a/CHANGELOG.md b/CHANGELOG.md index e0ed50dc5..d08482e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added utility functions `isStandardContextType(contextType: string)`, `isStandardIntent(intent: string)`,`getPossibleContextsForIntent(intent: StandardIntent)`. ([#1139](https://github.com/finos/FDC3/pull/1139)) * Added support for event listening outside of intent or context listnener. Added new function `addEventListener`, type `EventHandler`, enum `FDC3EventType` and interfaces `FDC3Event` and `FDC3ChannelChangedEvent`. ([#1207](https://github.com/finos/FDC3/pull/1207)) * Added new `CreateOrUpdateProfile` intent. ([#1359](https://github.com/finos/FDC3/pull/1359)) +* Added `clearContext` function to the `Channel` API, to be able to clear specific or all context types from the channel. ([#1379](https://github.com/finos/FDC3/pull/1379)) ### Changed diff --git a/docs/api/ref/Channel.md b/docs/api/ref/Channel.md index 4f949f09d..390a75138 100644 --- a/docs/api/ref/Channel.md +++ b/docs/api/ref/Channel.md @@ -36,6 +36,7 @@ interface Channel { broadcast(context: Context): Promise; getCurrentContext(contextType?: string): Promise; addContextListener(contextType: string | null, handler: ContextHandler): Promise; + clearContext(contextType?: string): Promise; //deprecated functions /** @@ -57,6 +58,7 @@ interface IChannel: IIntentResult Task Broadcast(IContext context); Task GetCurrentContext(string? contextType); Task AddContextListener(string? contextType, ContextHandler handler) where T : IContext; + Task ClearContext(string? contextType); } ``` @@ -431,6 +433,99 @@ catch (Exception ex) - [`broadcast`](#broadcast) - [`addContextListener`](#addcontextlistener) +### `clearContext` + + + + +```ts +public clearContext(contextType?: string): Promise; +``` + + + + +```csharp +Task ClearContext(string? contextType); +``` + + + + +Used to clear the specified context type if provided, otherwise, clear all context types present in the channel. The Desktop Agent MUST update its internal representation of the context in the channel and ensure that subsequent calls to [`getCurrentContext`](#getcurrentcontext) and any new joiners to that channel (through [`joinUserChannel`](DesktopAgent#joinUserChannel) or [`addContextListener`](DesktopAgent#addContextListener)) will not receive anything for either specified context type or the most recent context until new context has been broadcast to the channel. +Desktop Agents MUST also immediately broadcast the [`fdc3.nothing`](../../context/ref/Nothing.md/#nothing) type, which applications may listen to to be notified of the cleared context. If a `contextType` parameter was provided, then the `contextType` field will be set to that type, otherwise, it is omitted. + + +**Examples:** + +Without specifying a context type: + + + + +```ts +try { + const context = await channel.clearContext(); +} catch (err: ChannelError) { + // handle error +} +``` + + + + +```csharp +try +{ + var context = await channel.ClearContext(); +} +catch (Exception ex) +{ + // handle error +} +``` + + + + +Specifying a context type: + + + + +```ts +try { + const contact = await channel.clearContext('fdc3.contact'); +} catch (err: ChannelError) { + // handler error +} +``` + + + + +```csharp +try +{ + var context = await channel.ClearContext("fdc3.contact"); +} +catch (Exception ex) +{ + // handle error +} +``` + + + + + +**See also:** + +- [`getCurrentContext`](#getcurrentcontext) +- [`addContextListener`](DesktopAgent#addContextListener) +- [`joinUserChannel`](DesktopAgent#joinUserChannel) +- [`fdc3.nothing`](../../context/ref/Nothing.md/#nothing) + ## Deprecated Functions ### `addContextListener` (deprecated) diff --git a/docs/api/spec.md b/docs/api/spec.md index b15eec8ce..2de526ff6 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -244,7 +244,7 @@ When raising an intent a specific context is provided as input. The type of the 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 `IntentHandler` or in an App Directory). +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 `IntentHandler` or in an App Directory). This type is also used when the context is cleared for the channel. If the optional context type is provided when performing [`clearContext`](ref/Channel.md#clearcontext), that type will be recorded in the field `subType` of [`fdc3.nothing`](../context/ref/Nothing) context type. 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. @@ -595,7 +595,7 @@ Apps can join *User channels*. An app can only be joined to one User channel at 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. -When an app joins a User channel, or adds a context listener when already joined to a channel, it will automatically receive the current context for that channel. +When an app joins a User channel, or adds a context listener when already joined to a channel, it will automatically receive the current context for that channel, unless the context was cleared through [`clearContext`](ref/Channel.md#clearcontext). 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. @@ -840,6 +840,9 @@ The [Context specification](../context/spec#assumptions) recommends that complex 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. +### Context clearning on channels +Channel interface provides the ability to [`clearContext`](ref/Channel.md#clearcontext) on the channel, either for the specific context type, if provided, or for all contexts on that channel. Applications may listen for the `fdc3.nothing` type, or all context types, to be notified when the context is cleared. If a specific type was cleared, the `contextType` field of the [`fdc3.nothing`](../context/ref/Nothing) context type will be set with that type. Once cleared, any apps that join the channel, add new context listeners or call [`getCurrentContext`](ref/Channel.md#getcurrentcontext) will not return anything to the caller (other than the `fdc3.nothing` type indicating that context was cleared) until new context is broadcast to the channel. + ### Originating App Metadata Optional metadata about each context message received, including the app that originated the message, SHOULD be provided by the desktop agent implementation to registered context handlers on all types of channel. As this metadata is optional, apps making use of it MUST handle cases where it is not provided. diff --git a/schemas/context/nothing.schema.json b/schemas/context/nothing.schema.json index 2b970c895..fa69c23c6 100644 --- a/schemas/context/nothing.schema.json +++ b/schemas/context/nothing.schema.json @@ -10,6 +10,10 @@ "properties": { "type": { "const": "fdc3.nothing" + }, + "contextType": { + "type": "Context Type", + "description": "A referenced context type name. Used to indicate that a specific context type was cleared, if omitted, all contexts were cleared" } } }, @@ -17,7 +21,8 @@ ], "examples": [ { - "type": "fdc3.nothing" + "type": "fdc3.nothing", + "contextType": "fdc3.timeRange" } ] } \ No newline at end of file diff --git a/src/api/Channel.ts b/src/api/Channel.ts index 9eca774b5..9b58fc428 100644 --- a/src/api/Channel.ts +++ b/src/api/Channel.ts @@ -73,6 +73,15 @@ export interface Channel { */ addContextListener(contextType: string | null, handler: ContextHandler): Promise; + /** + * Clears context from the channel, and broadcasts an `fdc3.nothing` context to notify existing listeners that the context was cleared. Listeners added to the channel and calls to [`getCurrentContext`](#getcurrentcontext) will not receive any existing context until new context is broadcast to the channel. + * + * If a `contextType` is provided, only contexts of that type will be cleared and the `contextType` of the `fdc3.nothing` context will be set to that type name. + * + * If no `contextType` is provided, all contexts will be cleared and the `contextType` of the `fdc3.nothing` context will be omitted. + */ + clearContext(contextType?: string): Promise; + /** * @deprecated use `addContextListener(null, handler)` instead of `addContextListener(handler)`. */ diff --git a/toolbox/fdc3-conformance/App-Channel-Tests.md b/toolbox/fdc3-conformance/App-Channel-Tests.md index 597d0bc28..c2ec415e8 100644 --- a/toolbox/fdc3-conformance/App-Channel-Tests.md +++ b/toolbox/fdc3-conformance/App-Channel-Tests.md @@ -52,3 +52,6 @@ - `ACContextHistoryTyped`: Perform above test. - `ACContextHistoryMultiple`: **B** Broadcasts multiple history items of both types. Ensure that only the last version of each type is received by **A**. - `ACContextHistoryLast`: In step 5. **A** retrieves the _untyped_ current context of the channel via `const currentContext = await testChannel.getCurrentContext()`. Ensure that A receives only the very last broadcast context item _of any type_. +- `ACClearHistorySpecificContext`: Perform the above test, except after step 3 clear context with `await testChannel.clearContext('fdc3.instrument')`. Ensure that in step 5 nothing is received for `const instrument = await testChannel.getCurrentContext('fdc3.instrument')` and context is received successfully for `const instrument = await testChannel.getCurrentContext('fdc3.contact')` +- `ACClearHistoryAllContexts`: Perform the above test, except after step 3 clear context with `await testChannel.clearContext('fdc3.nothing')`. Ensure that in step 5 nothing is received for `const instrument = await testChannel.getCurrentContext('fdc3.instrument')` and for `const instrument = await testChannel.getCurrentContext('fdc3.contact')` +- `ACClearHistoryAllContextsSubscribedToNothing`: Perform the above test, except after step 3 clear context with `await testChannel.clearContext('fdc3.nothing')` and in step 4 call `await testChannel.addContextListener("fdc3.nothing", handler)` instead. Ensure that after clearing context, the handler is called.