Skip to content

Commit 745e503

Browse files
authored
Merge pull request #1645 from jaybhanushali3166/fix/bind-this-in-api-objects-and-docs-update
fix: bind this in API-returned objects & update docs for JS implementers
2 parents af07793 + a718c75 commit 745e503

15 files changed

Lines changed: 373 additions & 2 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## [Unreleased]
8+
* Enhanced method binding for FDC3 API objects to support destructuring. All public methods of `Channel`, `PrivateChannel`, and `IntentResolution` objects are now properly bound to their instances using `.bind(this)` in their constructors. ([#1645](https://github.com/finos/FDC3/issues/1645))
89

910
### Added
1011
* Add a notes field to Trade type ([#1563](https://github.com/finos/FDC3/pull/1563))

packages/fdc3-agent-proxy/src/channels/DefaultChannel.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export class DefaultChannel implements Channel {
3232
this.id = id;
3333
this.type = type;
3434
this.displayMetadata = displayMetadata;
35+
36+
//bind all functions to allow destructuring
37+
this.broadcast = this.broadcast.bind(this);
38+
this.getCurrentContext = this.getCurrentContext.bind(this);
39+
this.addContextListener = this.addContextListener.bind(this);
3540
}
3641

3742
async broadcast(context: Context): Promise<void> {

packages/fdc3-agent-proxy/src/channels/DefaultPrivateChannel.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ import {
2424
export class DefaultPrivateChannel extends DefaultChannel implements PrivateChannel {
2525
constructor(messaging: Messaging, messageExchangeTimeout: number, id: string) {
2626
super(messaging, messageExchangeTimeout, id, 'private');
27+
28+
//bind all functions to allow destructuring
29+
this.addContextListener = this.addContextListener.bind(this);
30+
this.addEventListener = this.addEventListener.bind(this);
31+
this.disconnect = this.disconnect.bind(this);
2732
}
2833

2934
async addEventListener(type: PrivateChannelEventTypes | null, handler: EventHandler): Promise<Listener> {

packages/fdc3-agent-proxy/src/intents/DefaultIntentResolution.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ export class DefaultIntentResolution implements IntentResolution {
1212
this.result = result;
1313
this.source = source;
1414
this.intent = intent;
15+
16+
//bind all functions to allow destructuring
17+
this.getResult = this.getResult.bind(this);
1518
}
1619

1720
getResult(): Promise<IntentResult> {

packages/fdc3-agent-proxy/test/features/app-channels.feature

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,39 @@ Feature: Channel Listeners Support
7878
And I call "{channel1}" with "addContextListener" with parameters "{true}" and "{resultHandler}"
7979
Then "{result}" is an error
8080
And I call "{channel1}" with "addContextListener" with parameters "{null}" and "{true}"
81-
Then "{result}" is an error
81+
Then "{result}" is an error
82+
83+
Scenario: Destructured channel methods - broadcast and addContextListener
84+
When I call "{api1}" with "getOrCreateChannel" with parameter "channel-name"
85+
And I refer to "{result}" as "channel1"
86+
And I destructure methods "addContextListener", "broadcast" from "{channel1}"
87+
And I call destructured "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
88+
And messaging receives "{instrumentMessageOne}"
89+
Then "{contexts}" is an array of objects with the following contents
90+
| id.ticker | type | name |
91+
| AAPL | fdc3.instrument | Apple |
92+
And messaging will have posts
93+
| payload.channelId | payload.contextType | matches_type |
94+
| channel-name | {null} | getOrCreateChannelRequest |
95+
| channel-name | fdc3.instrument | addContextListenerRequest |
96+
97+
Scenario: Destructured getCurrentContext after broadcast
98+
When I call "{api1}" with "getOrCreateChannel" with parameter "channel-name"
99+
And I refer to "{result}" as "channel1"
100+
And I destructure methods "broadcast", "getCurrentContext" from "{channel1}"
101+
And I call destructured "broadcast" with parameter "{instrumentContext}"
102+
And I call destructured "getCurrentContext" with parameter "fdc3.instrument"
103+
Then "{result}" is an object with the following contents
104+
| id.ticker | type | name |
105+
| AAPL | fdc3.instrument | Apple |
106+
107+
Scenario: Destructured listener receives filtered context
108+
Given "countryContext" is a "fdc3.country" context
109+
When I call "{api1}" with "getOrCreateChannel" with parameter "channel-name"
110+
And I refer to "{result}" as "channel1"
111+
And I destructure methods "addContextListener", "broadcast" from "{channel1}"
112+
And I call destructured "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
113+
And messaging receives "{instrumentMessageOne}"
114+
Then "{contexts}" is an array of objects with the following contents
115+
| id.ticker | type | name |
116+
| AAPL | fdc3.instrument | Apple |

packages/fdc3-agent-proxy/test/features/intent-results.feature

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,52 @@ Feature: Intents Can Return Different Results
7777
And messaging will have posts
7878
| payload.intent | payload.context.type | payload.context.id.ticker | matches_type |
7979
| OrderFood | fdc3.instrument | AAPL | raiseIntentRequest |
80+
81+
Scenario: Destructured getResult returns context data
82+
Given Raise Intent returns a context of "{instrumentContext}"
83+
When I destructure method "raiseIntent" from "{api}"
84+
And I call destructured "raiseIntent" with parameters "OrderFood" and "{instrumentContext}"
85+
And I destructure method "getResult" from "{result}"
86+
And I call destructured "getResult"
87+
Then "{result}" is an object with the following contents
88+
| type | name |
89+
| fdc3.instrument | Apple |
90+
And messaging will have posts
91+
| payload.intent | payload.context.type | payload.context.id.ticker | matches_type |
92+
| OrderFood | fdc3.instrument | AAPL | raiseIntentRequest |
93+
94+
Scenario: Destructured raiseIntent with app parameter
95+
When I destructure method "raiseIntent" from "{api}"
96+
And I call destructured "raiseIntent" with parameters "OrderFood" and "{instrumentContext}" and "{c1}"
97+
Then "{result}" is an object with the following contents
98+
| source.appId | source.instanceId | intent |
99+
| chipShop | c1 | OrderFood |
100+
And messaging will have posts
101+
| payload.intent | payload.context.type | payload.context.id.ticker | payload.app.appId | payload.app.instanceId | matches_type |
102+
| OrderFood | fdc3.instrument | AAPL | chipShop | c1 | raiseIntentRequest |
103+
104+
Scenario: Destructured getResult returns app channel
105+
Given Raise Intent returns an app channel
106+
When I destructure method "raiseIntent" from "{api}"
107+
And I call destructured "raiseIntent" with parameters "OrderFood" and "{instrumentContext}"
108+
And I destructure method "getResult" from "{result}"
109+
And I call destructured "getResult"
110+
Then "{result}" is an object with the following contents
111+
| type | id |
112+
| app | result-channel |
113+
And messaging will have posts
114+
| payload.intent | payload.context.type | payload.context.id.ticker | matches_type |
115+
| OrderFood | fdc3.instrument | AAPL | raiseIntentRequest |
116+
117+
Scenario: Destructured getResult returns private channel
118+
Given Raise Intent returns a private channel
119+
When I destructure method "raiseIntent" from "{api}"
120+
And I call destructured "raiseIntent" with parameters "OrderFood" and "{instrumentContext}"
121+
And I destructure method "getResult" from "{result}"
122+
And I call destructured "getResult"
123+
Then "{result}" is an object with the following contents
124+
| type | id |
125+
| private | result-channel |
126+
And messaging will have posts
127+
| payload.intent | payload.context.type | payload.context.id.ticker | matches_type |
128+
| OrderFood | fdc3.instrument | AAPL | raiseIntentRequest |

packages/fdc3-agent-proxy/test/features/open.feature

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,23 @@ Feature: Desktop Agent Information
3030
And messaging will have posts
3131
| payload.app.appId | payload.context.type | payload.context.id.ticker | matches_type |
3232
| nonExistent | fdc3.instrument | AAPL | openRequest |
33+
34+
Scenario: Open An App - Destructured
35+
When I destructure method "open" from "{api}"
36+
And I call destructured "open" with parameters "{c1}" and "{instrumentContext}"
37+
Then "{result}" is an object with the following contents
38+
| appId | instanceId |
39+
| chipShop | abc123 |
40+
And messaging will have posts
41+
| payload.app.appId | payload.context.type | payload.context.id.ticker | matches_type |
42+
| chipShop | fdc3.instrument | AAPL | openRequest |
43+
44+
Scenario: Open An App Using App ID - Destructured
45+
When I destructure method "open" from "{api}"
46+
And I call destructured "open" with parameters "chipShop" and "{instrumentContext}"
47+
Then "{result}" is an object with the following contents
48+
| appId | instanceId |
49+
| chipShop | abc123 |
50+
And messaging will have posts
51+
| payload.app.appId | payload.context.type | payload.context.id.ticker | matches_type |
52+
| chipShop | fdc3.instrument | AAPL | openRequest |

packages/fdc3-agent-proxy/test/features/private-channels.feature

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,28 @@ Feature: Basic Private Channels Support
112112
| payload.channelId | matches_type |
113113
| {null} | createPrivateChannelRequest |
114114
| {privateChannel.id} | privateChannelDisconnectRequest |
115+
116+
Scenario: Destructured createPrivateChannel works correctly
117+
When I destructure method "createPrivateChannel" from "{api}"
118+
And I call destructured "createPrivateChannel"
119+
And I refer to "{result}" as "destructuredPrivateChannel"
120+
Then messaging will have posts
121+
| payload.channelId | matches_type |
122+
| {null} | createPrivateChannelRequest |
123+
124+
Scenario: Destructured private channel methods work correctly
125+
Given "resultHandler" pipes context to "contexts"
126+
And I destructure methods "addContextListener", "broadcast" from "{privateChannel}"
127+
And I call destructured "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
128+
And I call destructured "broadcast" with parameter "{instrumentContext}"
129+
And messaging receives "{instrumentMessageOne}"
130+
Then "{contexts}" is an array of objects with the following contents
131+
| id.ticker | type | name |
132+
| AAPL | fdc3.instrument | Apple |
133+
134+
Scenario: Destructured disconnect works correctly
135+
When I destructure method "disconnect" from "{privateChannel}"
136+
And I call destructured "disconnect"
137+
Then messaging will have posts
138+
| payload.channelId | matches_type |
139+
| {privateChannel.id} | privateChannelDisconnectRequest |

packages/fdc3-agent-proxy/test/features/user-channels.feature

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,68 @@ Feature: Basic User Channels Support
235235
Given "typesHandler" pipes events to "types"
236236
When I call "{api}" with "addEventListener" with parameters "unknownEventType" and "{typesHandler}"
237237
Then "{result}" is an error with message "UnknownEventType"
238+
239+
Scenario: Destructured getUserChannels returns user channels
240+
When I destructure method "getUserChannels" from "{api}"
241+
And I call destructured "getUserChannels"
242+
Then "{result}" is an array of objects with the following contents
243+
| id | type | displayMetadata.color | displayMetadata.glyph | displayMetadata.name |
244+
| one | user | red | triangle | The one channel |
245+
| two | user | red | triangle | The two channel |
246+
| three | user | red | triangle | The three channel |
247+
And messaging will have posts
248+
| meta.source.appId | meta.source.instanceId | matches_type |
249+
| cucumber-app | cucumber-instance | getUserChannelsRequest |
250+
251+
Scenario: Destructured joinUserChannel and getCurrentChannel work correctly
252+
When I destructure method "joinUserChannel" from "{api}"
253+
And I call destructured "joinUserChannel" with parameter "one"
254+
And I destructure method "getCurrentChannel" from "{api}"
255+
And I call destructured "getCurrentChannel"
256+
Then "{result}" is an object with the following contents
257+
| id | type | displayMetadata.color |
258+
| one | user | red |
259+
And messaging will have posts
260+
| payload.channelId | matches_type |
261+
| one | joinUserChannelRequest |
262+
| {null} | getCurrentChannelRequest |
263+
264+
Scenario: Destructured channel getCurrentContext after broadcast
265+
Given "resultHandler" pipes context to "contexts"
266+
When I call "{api}" with "joinUserChannel" with parameter "one"
267+
And I call "{api}" with "getCurrentChannel"
268+
And I refer to "{result}" as "theChannel"
269+
And I destructure methods "broadcast", "getCurrentContext" from "{api}"
270+
And I destructure method "getCurrentContext" from "{theChannel}"
271+
And I call destructured "broadcast" with parameter "{instrumentContext}"
272+
And I call destructured "getCurrentContext"
273+
Then "{result}" is an object with the following contents
274+
| id.ticker | type | name |
275+
| AAPL | fdc3.instrument | Apple |
276+
277+
Scenario: Destructured broadcast on user channel
278+
Given "resultHandler" pipes context to "contexts"
279+
When I destructure method "broadcast" from "{api}"
280+
And I call "{api}" with "joinUserChannel" with parameter "one"
281+
And I call destructured "broadcast" with parameter "{instrumentContext}"
282+
And I call "{api}" with "getCurrentChannel"
283+
And I refer to "{result}" as "theChannel"
284+
And I call "{theChannel}" with "getCurrentContext"
285+
Then "{result}" is an object with the following contents
286+
| id.ticker | type | name |
287+
| AAPL | fdc3.instrument | Apple |
288+
289+
Scenario: Destructured user channel addContextListener works correctly
290+
Given "resultHandler" pipes context to "contexts"
291+
When I destructure method "addContextListener" from "{api}"
292+
And I call "{api}" with "joinUserChannel" with parameter "one"
293+
And I call destructured "addContextListener" with parameters "fdc3.instrument" and "{resultHandler}"
294+
And messaging receives "{instrumentMessageOne}"
295+
Then "{contexts}" is an array of objects with the following contents
296+
| id.ticker | type | name |
297+
| AAPL | fdc3.instrument | Apple |
298+
And messaging will have posts
299+
| payload.channelId | payload.contextType | matches_type |
300+
| one | {null} | joinUserChannelRequest |
301+
| {null} | {null} | getCurrentChannelRequest |
302+
| one | fdc3.instrument | addContextListenerRequest |

packages/fdc3-agent-proxy/test/step-definitions/channels.steps.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,80 @@ Given('User Channels one, two and three', function (this: CustomWorld) {
209209
three: [],
210210
};
211211
});
212+
213+
When(
214+
'I destructure methods {string}, {string} from {string}',
215+
function (this: CustomWorld, method1: string, method2: string, objectField: string) {
216+
const object = handleResolve(objectField, this);
217+
this.props[`destructured_${method1}`] = object[method1];
218+
this.props[`destructured_${method2}`] = object[method2];
219+
}
220+
);
221+
222+
When(
223+
'I destructure method {string} from {string}',
224+
function (this: CustomWorld, methodName: string, objectField: string) {
225+
const object = handleResolve(objectField, this);
226+
const destructuredMethod = object[methodName];
227+
this.props[`destructured_${methodName}`] = destructuredMethod;
228+
}
229+
);
230+
231+
When('I call destructured {string}', async function (this: CustomWorld, methodName: string) {
232+
const destructuredMethod = this.props[`destructured_${methodName}`];
233+
try {
234+
const result = await destructuredMethod();
235+
this.props['result'] = result;
236+
} catch (error) {
237+
this.props['error'] = error;
238+
this.props['result'] = null;
239+
}
240+
});
241+
242+
When(
243+
'I call destructured {string} with parameter {string}',
244+
async function (this: CustomWorld, methodName: string, param: string) {
245+
const destructuredMethod = this.props[`destructured_${methodName}`];
246+
const resolvedParam = handleResolve(param, this);
247+
try {
248+
const result = await destructuredMethod(resolvedParam);
249+
this.props['result'] = result;
250+
} catch (error) {
251+
this.props['error'] = error;
252+
this.props['result'] = null;
253+
}
254+
}
255+
);
256+
257+
When(
258+
'I call destructured {string} with parameters {string} and {string}',
259+
async function (this: CustomWorld, methodName: string, param1: string, param2: string) {
260+
const destructuredMethod = this.props[`destructured_${methodName}`];
261+
const resolvedParam1 = handleResolve(param1, this);
262+
const resolvedParam2 = handleResolve(param2, this);
263+
try {
264+
const result = await destructuredMethod(resolvedParam1, resolvedParam2);
265+
this.props['result'] = result;
266+
} catch (error) {
267+
this.props['error'] = error;
268+
this.props['result'] = null;
269+
}
270+
}
271+
);
272+
273+
When(
274+
'I call destructured {string} with parameters {string} and {string} and {string}',
275+
async function (this: CustomWorld, methodName: string, param1: string, param2: string, param3: string) {
276+
const destructuredMethod = this.props[`destructured_${methodName}`];
277+
const resolvedParam1 = handleResolve(param1, this);
278+
const resolvedParam2 = handleResolve(param2, this);
279+
const resolvedParam3 = handleResolve(param3, this);
280+
try {
281+
const result = await destructuredMethod(resolvedParam1, resolvedParam2, resolvedParam3);
282+
this.props['result'] = result;
283+
} catch (error) {
284+
this.props['error'] = error;
285+
this.props['result'] = null;
286+
}
287+
}
288+
);

0 commit comments

Comments
 (0)