-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
498 Allow intents to be resolved on output type (where they return data) #499
Closed
kriswest
wants to merge
22
commits into
finos:master
from
InteropIO:498-resolve-intents-on-output-type
Closed
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
167a7d4
Merge branch '432-return-data-from-an-intent' into 498-resolve-intent…
kriswest 85619ec
Add suport for metadata on output types for intent to appD and the fi…
kriswest 0adfd83
changelog
kriswest bd97624
WIP
kriswest ea69042
WIP
kriswest 22cd3be
Merge branch '432-return-data-from-an-intent' into 498-resolve-intent…
kriswest 6d68022
Changing IntentResolution.getData() to IntentResolution.getResult()
kriswest bb1e120
Changing IntentResolution.getData() to IntentResolution.getResult()
kriswest e3a962e
WIP
kriswest 240b424
WIP
kriswest 9b0b3b4
completed draft of feeds
kriswest 075067a
changelog
kriswest ea3e2eb
outputContext -> resultContext and other comments from review
kriswest 157a46f
Mergeing updates from review of upstream PR
kriswest 4c274a4
Apply suggestions from code review
kriswest 461bf95
Merge branch 'master' into 498-resolve-intents-on-output-type
kriswest 3fa81cb
Merge branch '498-resolve-intents-on-output-type' into 433-private-ch…
kriswest dbd3e81
Merge branch 'master' into 433-private-channels-returned-by-intents
kriswest ef1d315
Merge branch 'master' into 433-private-channels-returned-by-intents
kriswest 6412fba
Removing defunct paragraph from docs/api/spec.md (bad merge)
kriswest 1da923a
prettier
kriswest b88b4af
Update docs/api/ref/DesktopAgent.md
kriswest File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
--- | ||
id: PrivateChannel | ||
sidebar_label: PrivateChannel | ||
title: PrivateChannel | ||
hide_title: true | ||
--- | ||
# `PrivateChannel` | ||
|
||
Object representing a private context channel, which is intended to support secure communication between applications, and extends the Channel interface with event handlers which provide information on the connection state of both parties, ensuring that desktop agents do not need to queue or retain messages that are broadcast before a context listener is added and that applications are able to stop broadcasting messages when the other party has disconnected. | ||
|
||
It is intended that Desktop Agent implementations: | ||
- SHOULD restrict external apps from listening or publishing on this channel. | ||
- MUST prevent private channels from being retrieved via fdc3.getOrCreateChannel. | ||
- MUST provide the `id` value for the channel as required by the Channel interface. | ||
|
||
```ts | ||
interface PrivateChannel extends Channel { | ||
// methods | ||
onAddContextListener(handler: (contextType?: string) => void): Listener; | ||
onUnsubscribe(handler: (contextType?: string) => void): Listener; | ||
onDisconnect(handler: () => void): Listener; | ||
disconnect(): void; | ||
} | ||
``` | ||
|
||
#### See also | ||
|
||
* [`Channel`](Channel) | ||
* [`Listener`](Types#listener) | ||
* [`DesktopAgent.addIntentListener`](DesktopAgent#addintentlistener) | ||
* [`DesktopAgent.createPrivateChannel`](DesktopAgent#createPrivateChannel) | ||
* [`DesktopAgent.raiseIntent`](DesktopAgent#raiseintent) | ||
|
||
## Examples | ||
### 'Server-side' example: | ||
The intent app establishes and returns a PrivateChannel to the client (who is awaiting `getResult()`). When the client calls `addContextlistener()` on that channel, the intent app receives notice via the handler added with `onAddContextListener()` and knows that the client is ready to start receiving quotes. | ||
|
||
The Desktop Agent knows that a channel is being returned by inspecting the object returned from the handler (e.g. check constructor or look for private member). | ||
|
||
```javascript | ||
fdc3.addIntentListener("QuoteStream", async (context) => { | ||
const channel: PrivateChannel = await fdc3.createPrivateChannel(); | ||
const symbol = context.id.ticker; | ||
|
||
// This gets called when the remote side adds a context listener | ||
const addContextListener = channel.onAddContextListener((contextType) => { | ||
// broadcast price quotes as they come in from our quote feed | ||
feed.onQuote(symbol, (price) => { | ||
channel.broadcast({ type: "price", price}); | ||
}); | ||
}); | ||
|
||
// This gets called when the remote side calls Listener.unsubscribe() | ||
const unsubscriberListener = channel.onUnsubscribe((contextType) => { | ||
feed.stop(symbol); | ||
}); | ||
|
||
// This gets called if the remote side closes | ||
const disconnectListener = channel.onDisconnect(() => { | ||
feed.stop(symbol); | ||
}); | ||
|
||
return channel; | ||
}); | ||
``` | ||
|
||
### 'Client-side' example: | ||
|
||
```javascript | ||
try { | ||
const resolution3 = await fdc3.raiseIntent("QuoteStream", { type: "fdc3.instrument", id : { symbol: "AAPL"}}); | ||
try { | ||
const result = await resolution3.getResult(); | ||
//check that we got a result and that its a channel | ||
if (result && result.addContextListener) { | ||
const listener = result.addContextListener("price", (quote) => console.log(quote)); | ||
result.onDisconnect(() => { | ||
console.warn("Quote feed went down"); | ||
}); | ||
|
||
// Sometime later... | ||
listener.unsubscribe(); | ||
} else { | ||
console.warn(`${resolution3.source} did not return a channel`); | ||
} | ||
} catch(channelError){ | ||
console.log(`Error: ${resolution3.source} returned an error: ${channelError}`); | ||
} | ||
} catch (resolverError) { | ||
console.error(`Error: Intent was not resolved: ${resolverError}`); | ||
} | ||
``` | ||
|
||
|
||
## Methods | ||
|
||
### `onAddContextListener` | ||
```ts | ||
onAddContextListener(handler: (contextType?: string) => void): Listener; | ||
``` | ||
Adds a listener that will be called each time that the remote app invokes addContextListener on this channel. | ||
|
||
Desktop Agents MUST call this for each invokation of addContextListener on this channel, including those that occurred before this handler was registered (to prevent race conditions). | ||
|
||
#### See also | ||
* [`Channel.addContextListener`](Channel#addcontextlistener) | ||
|
||
### `onUnsubscribe` | ||
|
||
```ts | ||
onUnsubscribe(handler: (contextType?: string) => void): Listener; | ||
``` | ||
|
||
Adds a listener that will be called whenever the remote app invokes `Listener.unsubscribe()` on a context listener that it previously added. | ||
|
||
Desktop Agents MUST call this when disconnect() is called by the other party, for each listner that they had added. | ||
|
||
#### See also | ||
* [`Listener`](Types#listener) | ||
|
||
### `onDisconnect` | ||
|
||
```ts | ||
onDisconnect(handler: () => void): Listener; | ||
``` | ||
|
||
Adds a listener that will be called when the remote app terminates, for example when its window is closed or because disconnect was called. This is in addition to calls that will be made to an onUnsubscribe listeners. | ||
|
||
#### See also | ||
* [`disconnect`](#disconnect) | ||
|
||
### `disconnect` | ||
|
||
```ts | ||
disconnect(): void; | ||
``` | ||
|
||
May be called to indicate that a participant will no longer interact with this channel. | ||
|
||
After this function has been called, Desktop Agents SHOULD prevent apps from broadcasting on this channel and MUST automatically call Listener.unsubscribe() for each listener that they've added (causing any `onUnsubscribe` handler added by the other party to be called) before triggering any onDisconnect handler added by the other party. | ||
|
||
#### See also | ||
* [`onUnsubscribe`](#onunsubscribe) | ||
* [`Listener`](Types#listener) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may need some more discussion around error handling. For instance, a request for a quote would likely return some sort of well-defined "Quote" context, but what if the end user is not authorized to receive that market data? We wouldn't want to muddy the Quote context with arbitrary error messages, but then the DataError enum type provides no information about why the intent was rejected, which in this case would result in quite a bit of potential confusion for the end user.
We may want to either (a) consider allowing at least a simple error string to be rejected/thrown, or (b) allow for the possibility of more than one context being returned (e.g. an Error context. Receiving apps may then disambiguate by examining the type member).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At present, an app resolving an intent has no means to identify the app raising the intent and couldn't assess authorization. Hence, any such mechanism would have to be a function of the Desktop Agent. However, I take the point on returning some info about why the promise was rejected (for feedback to the end-user - 'IntentHandlerRejected' is not all that informative and we are deliberately losing any info on why the rejection occurred).
I don't particularly like the idea of using an 'ErrorContext' type (I think that overloads the purpose of context). However, we could use the javascript Error type (or similar that we define), setting the message to these values and setting the optional
cause
property to the error/rejection message returned by the intent handler... However, its a departure from all other rejects, which reject with just a string.