-
Notifications
You must be signed in to change notification settings - Fork 129
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
Async listener and private channel events #1305
Changes from 4 commits
1a76a9b
439b49d
79c503a
e72eabf
c47b7b2
2bd62fa
8654cbe
5719b33
313f862
9e3bf09
889852d
32722dc
4f027b8
a07e776
2ed30e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,16 +11,19 @@ Object representing a private context channel, which is intended to support secu | |
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 prevent `PrivateChannels` from being retrieved via `fdc3.getOrCreateChannel`. | ||
- MUST provide the `id` value for the channel as required by the `Channel` interface. | ||
|
||
```ts | ||
interface PrivateChannel extends Channel { | ||
// methods | ||
// functions | ||
addEventListener(type: PrivateChannelEventType | null, handler: EventHandler): Promise<Listener>; | ||
disconnect(): Promise<void>; | ||
|
||
//deprecated functions | ||
onAddContextListener(handler: (contextType?: string) => void): Listener; | ||
onUnsubscribe(handler: (contextType?: string) => void): Listener; | ||
onDisconnect(handler: () => void): Listener; | ||
disconnect(): void; | ||
} | ||
``` | ||
|
||
|
@@ -36,34 +39,43 @@ interface PrivateChannel extends Channel { | |
|
||
### '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 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 `addEventListener()` 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). | ||
|
||
Although this interaction occurs entirely in frontend code, we refer to it as the 'server-side' interaction as it receives a request and initiates a stream of responses. | ||
|
||
```typescript | ||
```ts | ||
fdc3.addIntentListener("QuoteStream", async (context) => { | ||
const channel: PrivateChannel = await fdc3.createPrivateChannel(); | ||
const symbol = context.id.ticker; | ||
|
||
// 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}); | ||
}); | ||
}); | ||
const addContextListener = channel.addEventListener("addContextListener", | ||
(event: PrivateChannelAddContextListenerEvent) => { | ||
console.log(`remote side added a listener for ${event.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 unsubscribeListener = channel.onUnsubscribe((contextType) => { | ||
feed.stop(symbol); | ||
}); | ||
const unsubscribeListener = channel.addEventListener("unsubscribe", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bingenito need to replicate switch to new function in .NET version below |
||
(event: PrivateChannelUnsubscribeEvent) => { | ||
console.log(`remote side unsubscribed a listener for ${event.contextType}`); | ||
feed.stop(symbol); | ||
} | ||
); | ||
|
||
// This gets called if the remote side closes | ||
const disconnectListener = channel.onDisconnect(() => { | ||
feed.stop(symbol); | ||
}); | ||
const disconnectListener = channel.addEventListener("disconnect", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bingenito need to replicate switch to new function in .NET version below |
||
() => { | ||
console.log(`remote side disconnected`); | ||
feed.stop(symbol); | ||
} | ||
); | ||
|
||
return channel; | ||
}); | ||
|
@@ -75,88 +87,106 @@ The 'client' application retrieves a `Channel` by raising an intent with context | |
|
||
Although this interaction occurs entirely in frontend code, we refer to it as the 'client-side' interaction as it requests and receives a stream of responses. | ||
|
||
```javascript | ||
```ts | ||
try { | ||
const resolution3 = await fdc3.raiseIntent("QuoteStream", { type: "fdc3.instrument", id : { symbol: "AAPL" } }); | ||
try { | ||
const result = await resolution3.getResult(); | ||
//check that we got a result and that it's a channel | ||
if (result && result.addContextListener) { | ||
const listener = result.addContextListener("price", (quote) => console.log(quote)); | ||
|
||
//if it's a PrivateChannel | ||
if (result.onDisconnect) { | ||
result.onDisconnect(() => { | ||
console.warn("Quote feed went down"); | ||
}); | ||
const result = await resolution3.getResult(); | ||
//check that we got a result and that it's a channel | ||
if (result && result.addContextListener) { | ||
const listener = result.addContextListener("price", (quote) => console.log(quote)); | ||
//if it's a PrivateChannel | ||
if (result.type == "private") { | ||
result.addEventListener("disconnect", () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bingenito need to replicate switch to new function in .NET version below |
||
console.warn("Quote feed went down"); | ||
}); | ||
|
||
// Sometime later... | ||
listener.unsubscribe(); | ||
} | ||
await listener.unsubscribe(); | ||
} | ||
} else { | ||
console.warn(`${resolution3.source} did not return a channel`); | ||
} | ||
} catch(channelError) { | ||
console.log(`Error: ${resolution3.source} returned an error: ${channelError}`); | ||
console.log(`Error: ${resolution3.source} returned an error: ${channelError}`); | ||
} | ||
} catch (resolverError) { | ||
console.error(`Error: Intent was not resolved: ${resolverError}`); | ||
} | ||
``` | ||
|
||
## Methods | ||
## Functions | ||
|
||
### `onAddContextListener` | ||
### `addEventListener` | ||
|
||
```ts | ||
onAddContextListener(handler: (contextType?: string) => void): Listener; | ||
addEventListener(type: PrivateChannelEventType | null, handler: EventHandler): Promise<Listener>; | ||
``` | ||
|
||
Adds a listener that will be called each time that the remote app invokes addContextListener on this channel. | ||
Register a handler for events from the PrivateChannel. Whenever the handler function is called it will be passed an event object with details related to the event. | ||
|
||
```ts | ||
// any event type | ||
const listener: Listener = await myPrivateChannel.addEventListener(null, | ||
(event: PrivateChannelEvent) => { | ||
console.log(`Received event ${event.type}\n\tDetails: ${event.details}`); | ||
} | ||
); | ||
|
||
Desktop Agents MUST call this for each invocation of addContextListener on this channel, including those that occurred before this handler was registered (to prevent race conditions). | ||
// listener for a specific event type | ||
const channelChangedListener: Listener = await myPrivateChannel.addEventListener( | ||
PrivateChannelEventType.ADD_CONTEXT_LISTENER, | ||
(event: PrivateChannelAddContextListenerEvent) => { ... } | ||
); | ||
``` | ||
|
||
**See also:** | ||
|
||
- [`Channel.addContextListener`](Channel#addcontextlistener) | ||
- [`PrivateChannelEventType`](./Events#privatechanneleventtype) | ||
- [`EventHandler`](./Events#eventhandler) | ||
|
||
### `onUnsubscribe` | ||
### `disconnect` | ||
|
||
```ts | ||
onUnsubscribe(handler: (contextType?: string) => void): Listener; | ||
disconnect(): Promise<void>; | ||
``` | ||
|
||
Adds a listener that will be called whenever the remote app invokes `Listener.unsubscribe()` on a context listener that it previously added. | ||
May be called to indicate that a participant will no longer interact with this channel. | ||
|
||
Desktop Agents MUST call this when disconnect() is called by the other party, for each listener that they had added. | ||
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 event handler for `unsubscribe` events added by the other party to be called) before triggering any handlers for `disconnect` events added by the other party. | ||
|
||
**See also:** | ||
|
||
- [`Listener`](Types#listener) | ||
|
||
### `onDisconnect` | ||
## Deprecated Functions | ||
|
||
### `onAddContextListener` | ||
|
||
```ts | ||
onDisconnect(handler: () => void): Listener; | ||
onAddContextListener(handler: (contextType?: string) => 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 onUnsubscribe listeners. | ||
|
||
**See also:** | ||
Deprecated in favour of the async `addEventListener("addContextListener", handler)` function. | ||
|
||
- [`disconnect`](#disconnect) | ||
Adds a listener that will be called each time that the remote app invokes addContextListener on this channel. | ||
|
||
### `disconnect` | ||
### `onUnsubscribe` | ||
|
||
```ts | ||
disconnect(): void; | ||
onUnsubscribe(handler: (contextType?: string) => void): Listener; | ||
``` | ||
|
||
May be called to indicate that a participant will no longer interact with this channel. | ||
Deprecated in favour of the async `addEventListener("unsubscribe", handler)` function. | ||
|
||
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. | ||
Adds a listener that will be called whenever the remote app invokes `Listener.unsubscribe()` on a context listener that it previously added. | ||
|
||
**See also:** | ||
### `onDisconnect` | ||
|
||
- [`onUnsubscribe`](#onunsubscribe) | ||
- [`Listener`](Types#listener) | ||
```ts | ||
onDisconnect(handler: () => void): Listener; | ||
``` | ||
|
||
Deprecated in favour of the aysnc `addEventListener("disconnect", handler)` function. | ||
bingenito marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Adds a listener that will be called when the remote app terminates, for example when its window is closed or because disconnect was called. |
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.
@bingenito need to replicate switch to new function in .NET version below