Skip to content

Commit 097fa39

Browse files
authored
Merge pull request #1139 from andreifloricel/master
Refactors ContextTypes&Intent to union type instead of enum
2 parents 4005274 + 17cf9e6 commit 097fa39

11 files changed

Lines changed: 249 additions & 23 deletions

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api/DesktopAgent.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { ImplementationMetadata } from './ImplementationMetadata';
1313
import { PrivateChannel } from './PrivateChannel';
1414
import { AppIdentifier } from './AppIdentifier';
1515
import { AppMetadata } from './AppMetadata';
16+
import { Intent } from '../intents/Intents';
17+
import { ContextType } from '../context/ContextType';
1618

1719
/**
1820
* A Desktop Agent is a desktop component (or aggregate of components) that serves as a
@@ -125,7 +127,7 @@ export interface DesktopAgent {
125127
* // }
126128
* ```
127129
*/
128-
findIntent(intent: string, context?: Context, resultType?: string): Promise<AppIntent>;
130+
findIntent(intent: Intent, context?: Context, resultType?: string): Promise<AppIntent>;
129131

130132
/**
131133
* Find all the available intents for a particular context, and optionally a desired result context type.
@@ -234,7 +236,7 @@ export interface DesktopAgent {
234236
*
235237
* If you wish to raise an Intent without a context, use the `fdc3.nothing` context type. This type exists so that apps can explicitly declare support for raising an intent without context.
236238
*
237-
* Returns an `IntentResolution` object with details of the app instance that was selected (or started) to respond to the intent.
239+
* Returns an `IntentResolution` object with details of the app instance that was selected (or started) to respond to the intent.
238240
*
239241
* Issuing apps may optionally wait on the promise that is returned by the `getResult()` member of the `IntentResolution`. This promise will resolve when the _receiving app's_ intent handler function returns and resolves a promise. The Desktop Agent resolves the issuing app's promise with the Context object, Channel object or void that is provided as resolution within the receiving app. The Desktop Agent MUST reject the issuing app's promise, with a string from the `ResultError` enumeration, if: (1) the intent handling function's returned promise rejects, (2) the intent handling function doesn't return a valid response (a promise or void), or (3) the returned promise resolves to an invalid type.
240242
*
@@ -268,7 +270,7 @@ export interface DesktopAgent {
268270
* }
269271
* ```
270272
*/
271-
raiseIntent(intent: string, context: Context, app?: AppIdentifier): Promise<IntentResolution>;
273+
raiseIntent(intent: Intent, context: Context, app?: AppIdentifier): Promise<IntentResolution>;
272274

273275
/**
274276
* Finds and raises an intent against apps registered with the desktop agent based on the type of the specified context data example.
@@ -346,7 +348,7 @@ export interface DesktopAgent {
346348
* });
347349
* ```
348350
*/
349-
addIntentListener(intent: string, handler: IntentHandler): Promise<Listener>;
351+
addIntentListener(intent: Intent, handler: IntentHandler): Promise<Listener>;
350352

351353
/**
352354
* Adds a listener for incoming context broadcasts from the Desktop Agent (via a User channel or `fdc3.open`API call. If the consumer is only interested in a context of a particular type, they can they can specify that type. If the consumer is able to receive context of any type or will inspect types received, then they can pass `null` as the `contextType` parameter to receive all context types.
@@ -371,7 +373,7 @@ export interface DesktopAgent {
371373
* });
372374
* ```
373375
*/
374-
addContextListener(contextType: string | null, handler: ContextHandler): Promise<Listener>;
376+
addContextListener(contextType: ContextType | null, handler: ContextHandler): Promise<Listener>;
375377

376378
/**
377379
* Retrieves a list of the User channels available for the app to join.
@@ -561,7 +563,7 @@ export interface DesktopAgent {
561563
* await fdc3.raiseIntent("StartChat", context, appIntent.apps[0].name);
562564
* ```
563565
*/
564-
raiseIntent(intent: string, context: Context, name: string): Promise<IntentResolution>;
566+
raiseIntent(intent: Intent, context: Context, name: string): Promise<IntentResolution>;
565567

566568
/**
567569
* @deprecated version of `raiseIntentForContext` that targets an app by by name rather than `AppIdentifier`. Provided for backwards compatibility with versions FDC3 standard <2.0.

src/api/IntentMetadata.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
* Copyright FINOS FDC3 contributors - see NOTICE file
44
*/
55

6+
import { Intent } from '../intents/Intents';
7+
68
/**
79
* Intent descriptor
810
*/
911
export interface IntentMetadata {
1012
/** The unique name of the intent that can be invoked by the raiseIntent call */
11-
readonly name: string;
13+
readonly name: Intent;
1214

1315
/** Display name for the intent.
1416
* @deprecated Use the intent name for display as display name may vary for

src/api/IntentResolution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { IntentResult } from './Types';
77
import { AppIdentifier } from './AppIdentifier';
8+
import { Intent } from '../intents/Intents';
89

910
/**
1011
* IntentResolution provides a standard format for data returned upon resolving an intent.
@@ -43,7 +44,7 @@ export interface IntentResolution {
4344
* The intent that was raised. May be used to determine which intent the user
4445
* chose in response to `fdc3.raiseIntentForContext()`.
4546
*/
46-
readonly intent: string;
47+
readonly intent: Intent;
4748
/**
4849
* Retrieves a promise that will resolve to `Context` data returned
4950
* by the application that resolves the raised intent, a `Channel`

src/api/Methods.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ import {
1414
ImplementationMetadata,
1515
AppMetadata,
1616
PrivateChannel,
17+
Intent,
18+
StandardContextType,
19+
StandardIntent,
20+
ContextType,
1721
} from '..';
22+
import { StandardContextsSet } from '../internal/contextConfiguration';
23+
import { StandardIntentsSet } from '../internal/intentConfiguration';
1824

1925
const DEFAULT_TIMEOUT = 5000;
2026

@@ -74,7 +80,7 @@ export function open(app: AppIdentifier | string, context?: Context): Promise<Ap
7480
}
7581
}
7682

77-
export function findIntent(intent: string, context?: Context, resultType?: string): Promise<AppIntent> {
83+
export function findIntent(intent: Intent, context?: Context, resultType?: string): Promise<AppIntent> {
7884
return rejectIfNoGlobal(() => window.fdc3.findIntent(intent, context, resultType));
7985
}
8086

@@ -86,7 +92,7 @@ export function broadcast(context: Context): Promise<void> {
8692
return rejectIfNoGlobal(() => window.fdc3.broadcast(context));
8793
}
8894

89-
export function raiseIntent(intent: string, context: Context, app?: AppIdentifier | string): Promise<IntentResolution> {
95+
export function raiseIntent(intent: Intent, context: Context, app?: AppIdentifier | string): Promise<IntentResolution> {
9096
if (isString(app)) {
9197
return rejectIfNoGlobal(() => window.fdc3.raiseIntent(intent, context, app));
9298
} else {
@@ -102,12 +108,12 @@ export function raiseIntentForContext(context: Context, app?: AppIdentifier | st
102108
}
103109
}
104110

105-
export function addIntentListener(intent: string, handler: IntentHandler): Promise<Listener> {
111+
export function addIntentListener(intent: Intent, handler: IntentHandler): Promise<Listener> {
106112
return rejectIfNoGlobal(() => window.fdc3.addIntentListener(intent, handler));
107113
}
108114

109115
export function addContextListener(
110-
contextTypeOrHandler: string | null | ContextHandler,
116+
contextTypeOrHandler: ContextType | null | ContextHandler,
111117
handler?: ContextHandler
112118
): Promise<Listener> {
113119
//Handle (deprecated) function signature that allowed contextType argument to be omitted
@@ -178,6 +184,22 @@ export function findInstances(app: AppIdentifier): Promise<AppIdentifier[]> {
178184
return rejectIfNoGlobal(() => window.fdc3.findInstances(app));
179185
}
180186

187+
/**
188+
* Check if the given context is a standard context type.
189+
* @param contextType
190+
*/
191+
export function isStandardContextType(contextType: ContextType): contextType is StandardContextType {
192+
return StandardContextsSet.has(contextType as StandardContextType);
193+
}
194+
195+
/**
196+
* Check if the given intent is a standard intent.
197+
* @param intent
198+
*/
199+
export function isStandardIntent(intent: Intent): intent is StandardIntent {
200+
return StandardIntentsSet.has(intent as StandardIntent);
201+
}
202+
181203
/**
182204
* Compare numeric semver version number strings (in the form `1.2.3`).
183205
*
@@ -222,5 +244,5 @@ export const versionIsAtLeast: (metadata: ImplementationMetadata, version: strin
222244
version
223245
) => {
224246
let comparison = compareVersionNumbers(metadata.fdc3Version, version);
225-
return comparison === null ? null : comparison >= 0 ? true : false;
247+
return comparison === null ? null : comparison >= 0;
226248
};

src/context/ContextType.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,52 @@
22
* SPDX-License-Identifier: Apache-2.0
33
* Copyright FINOS FDC3 contributors - see NOTICE file
44
*/
5+
6+
/**
7+
* @see https://fdc3.finos.org/docs/context/spec#standard-context-types
8+
*/
9+
export type StandardContextType =
10+
| 'fdc3.action'
11+
| 'fdc3.chart'
12+
| 'fdc3.chat.initSettings'
13+
| 'fdc3.chat.message'
14+
| 'fdc3.chat.room'
15+
| 'fdc3.chat.searchCriteria'
16+
| 'fdc3.contact'
17+
| 'fdc3.contactList'
18+
| 'fdc3.country'
19+
| 'fdc3.currency'
20+
| 'fdc3.email'
21+
| 'fdc3.instrument'
22+
| 'fdc3.instrumentList'
23+
| 'fdc3.interaction'
24+
| 'fdc3.message'
25+
| 'fdc3.organization'
26+
| 'fdc3.portfolio'
27+
| 'fdc3.position'
28+
| 'fdc3.nothing'
29+
| 'fdc3.timerange'
30+
| 'fdc3.transactionResult'
31+
| 'fdc3.valuation';
32+
33+
/**
34+
* @see https://fdc3.finos.org/docs/context/spec#standard-context-types
35+
*/
36+
export type ExperimentalContextType =
37+
| 'fdc3.order'
38+
| 'fdc3.orderList'
39+
| 'fdc3.product'
40+
| 'fdc3.trade'
41+
| 'fdc3.tradeList';
42+
43+
/**
44+
* @see https://fdc3.finos.org/docs/context/spec
45+
*/
46+
export type ContextType = StandardContextType | ExperimentalContextType | (string & {});
47+
48+
/**
49+
* @deprecated Use {@link StandardContextType} instead
50+
*/
551
export enum ContextTypes {
652
Chart = 'fdc3.chart',
753
ChatInitSettings = 'fdc3.chat.initSettings',
@@ -23,5 +69,3 @@ export enum ContextTypes {
2369
TransactionResult = 'fdc3.transactionResult',
2470
Valuation = 'fdc3.valuation',
2571
}
26-
27-
export type ContextType = ContextTypes | string;

src/intents/Intents.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,38 @@
22
* SPDX-License-Identifier: Apache-2.0
33
* Copyright FINOS FDC3 contributors - see NOTICE file
44
*/
5+
6+
/**
7+
* @see https://fdc3.finos.org/docs/intents/spec#standard-intents
8+
*/
9+
export type StandardIntent =
10+
| 'CreateInteraction'
11+
| 'SendChatMessage'
12+
| 'StartCall'
13+
| 'StartChat'
14+
| 'StartEmail'
15+
| 'ViewAnalysis'
16+
| 'ViewChat'
17+
| 'ViewChart'
18+
| 'ViewContact'
19+
| 'ViewHoldings'
20+
| 'ViewInstrument'
21+
| 'ViewInteractions'
22+
| 'ViewMessages'
23+
| 'ViewNews'
24+
| 'ViewOrders'
25+
| 'ViewProfile'
26+
| 'ViewQuote'
27+
| 'ViewResearch';
28+
29+
/**
30+
* @see https://fdc3.finos.org/docs/intents/spec
31+
*/
32+
export type Intent = StandardIntent | (string & {});
33+
34+
/**
35+
* @deprecated Use {@link StandardIntent} instead
36+
*/
537
export enum Intents {
638
CreateInteraction = 'CreateInteraction',
739
SendChatMessage = 'SendChatMessage',
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { StandardContextType } from '../context/ContextType';
2+
import { exhaustiveStringTuple } from './typeHelpers';
3+
4+
const STANDARD_CONTEXT_TYPES = exhaustiveStringTuple<StandardContextType>()(
5+
'fdc3.action',
6+
'fdc3.chart',
7+
'fdc3.chat.initSettings',
8+
'fdc3.chat.message',
9+
'fdc3.chat.room',
10+
'fdc3.chat.searchCriteria',
11+
'fdc3.contact',
12+
'fdc3.contactList',
13+
'fdc3.country',
14+
'fdc3.currency',
15+
'fdc3.email',
16+
'fdc3.instrument',
17+
'fdc3.instrumentList',
18+
'fdc3.interaction',
19+
'fdc3.message',
20+
'fdc3.organization',
21+
'fdc3.portfolio',
22+
'fdc3.position',
23+
'fdc3.nothing',
24+
'fdc3.timerange',
25+
'fdc3.transactionResult',
26+
'fdc3.valuation'
27+
);
28+
29+
// used internally to check if a given intent/context is a standard one
30+
export const StandardContextsSet = new Set(STANDARD_CONTEXT_TYPES);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { StandardIntent } from '../intents/Intents';
2+
import { exhaustiveStringTuple } from './typeHelpers';
3+
4+
const STANDARD_INTENTS = exhaustiveStringTuple<StandardIntent>()(
5+
'CreateInteraction',
6+
'SendChatMessage',
7+
'StartCall',
8+
'StartChat',
9+
'StartEmail',
10+
'ViewAnalysis',
11+
'ViewChat',
12+
'ViewChart',
13+
'ViewContact',
14+
'ViewHoldings',
15+
'ViewInstrument',
16+
'ViewInteractions',
17+
'ViewMessages',
18+
'ViewNews',
19+
'ViewOrders',
20+
'ViewProfile',
21+
'ViewQuote',
22+
'ViewResearch'
23+
);
24+
25+
// used internally to check if a given intent/context is a standard one
26+
export const StandardIntentsSet = new Set(STANDARD_INTENTS);

src/internal/typeHelpers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
type AtLeastOne<T> = [T, ...T[]];
2+
3+
/**
4+
* Ensures at compile time that the given string tuple is exhaustive on a given union type, i.e. contains ALL possible values of the given UNION_TYPE.
5+
*/
6+
export const exhaustiveStringTuple = <UNION_TYPE extends string>() => <L extends AtLeastOne<UNION_TYPE>>(
7+
...tuple: L extends any
8+
? Exclude<UNION_TYPE, L[number]> extends never
9+
? L
10+
: Exclude<UNION_TYPE, L[number]>[]
11+
: never
12+
) => tuple;

0 commit comments

Comments
 (0)