Skip to content

Commit a82e280

Browse files
refactor(web-client): introduce ConfigBuilder for a better connection API (#749)
1 parent c09531e commit a82e280

12 files changed

Lines changed: 314 additions & 98 deletions

File tree

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
type ExtensionValue = { Pcb: string } | { KdcProxyUrl: string } | { DisplayControl: boolean };
2+
3+
export class Extension {
4+
static init(ident: string, value: unknown): ExtensionValue {
5+
switch (ident) {
6+
case 'Pcb':
7+
if (typeof value === 'string') {
8+
return { Pcb: value };
9+
} else {
10+
throw new Error('Pcb must be a string');
11+
}
12+
case 'KdcProxyUrl':
13+
if (typeof value === 'string') {
14+
return { KdcProxyUrl: value };
15+
} else {
16+
throw new Error('KdcProxyUrl must be a string');
17+
}
18+
case 'DisplayControl':
19+
if (typeof value === 'boolean') {
20+
return { DisplayControl: value };
21+
} else {
22+
throw new Error('DisplayControl must be a boolean');
23+
}
24+
default:
25+
throw new Error(`Invalid extension type: ${ident}`);
26+
}
27+
}
28+
}

web-client/iron-remote-desktop-rdp/src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import init, {
1010
ClipboardTransaction,
1111
ClipboardContent,
1212
} from '../../../crates/ironrdp-web/pkg/ironrdp_web';
13+
import { Extension } from './interfaces/Extension';
1314

1415
export default {
1516
init,
@@ -23,4 +24,5 @@ export default {
2324
ClipboardContent,
2425
Session,
2526
SessionTerminationInfo,
27+
Extension,
2628
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { ExtensionValue } from './ExtensionValue';
2+
3+
export interface Extension {
4+
init(ident: string, value: unknown): ExtensionValue;
5+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type ExtensionValue = unknown;

web-client/iron-remote-desktop/src/interfaces/RemoteDesktopModule.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { SessionBuilder } from './SessionBuilder';
77
import type { SessionTerminationInfo } from './SessionTerminationInfo';
88
import type { ClipboardTransaction } from './ClipboardTransaction';
99
import type { ClipboardContent } from './ClipboardContent';
10+
import type { Extension } from './Extension';
1011

1112
export interface RemoteDesktopModule {
1213
init: () => Promise<unknown>;
@@ -20,4 +21,5 @@ export interface RemoteDesktopModule {
2021
SessionTerminationInfo: SessionTerminationInfo;
2122
ClipboardTransaction: ClipboardTransaction;
2223
ClipboardContent: ClipboardContent;
24+
Extension: Extension;
2325
}

web-client/iron-remote-desktop/src/interfaces/UserInteraction.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
11
import type { ScreenScale } from '../enums/ScreenScale';
22
import type { NewSessionInfo } from './NewSessionInfo';
33
import type { SessionEvent } from './session-event';
4-
import type { DesktopSize } from './DesktopSize';
4+
import { ConfigBuilder } from '../services/ConfigBuilder';
5+
import type { Config } from '../services/Config';
56

67
export interface UserInteraction {
78
setVisibility(state: boolean): void;
89

910
setScale(scale: ScreenScale): void;
1011

11-
connect(
12-
username: string,
13-
password: string,
14-
destination: string,
15-
proxyAddress: string,
16-
serverDomain: string,
17-
authToken: string,
18-
desktopSize?: DesktopSize,
19-
preConnectionBlob?: string,
20-
kdc_proxy_url?: string,
21-
use_display_control?: boolean,
22-
): Promise<NewSessionInfo>;
12+
configBuilder(): ConfigBuilder;
13+
14+
connect(config: Config): Promise<NewSessionInfo>;
2315

2416
setKeyboardUnicodeMode(use_unicode: boolean): void;
2517

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { DesktopSize } from '../interfaces/DesktopSize';
2+
import type { ExtensionValue } from '../interfaces/ExtensionValue';
3+
4+
export class Config {
5+
readonly username: string;
6+
readonly password: string;
7+
readonly destination: string;
8+
readonly proxyAddress: string;
9+
readonly serverDomain: string;
10+
readonly authToken: string;
11+
readonly desktopSize?: DesktopSize;
12+
readonly extensions: ExtensionValue[];
13+
14+
constructor(
15+
userData: { username: string; password: string },
16+
proxyData: { address: string; authToken: string },
17+
configOptions: {
18+
destination: string;
19+
serverDomain: string;
20+
extensions: ExtensionValue[];
21+
desktopSize?: DesktopSize;
22+
},
23+
) {
24+
this.username = userData.username;
25+
this.password = userData.password;
26+
this.proxyAddress = proxyData.address;
27+
this.authToken = proxyData.authToken;
28+
this.destination = configOptions.destination;
29+
this.serverDomain = configOptions.serverDomain;
30+
this.extensions = configOptions.extensions;
31+
this.desktopSize = configOptions.desktopSize;
32+
}
33+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import type { DesktopSize } from '../interfaces/DesktopSize';
2+
import { Config } from './Config';
3+
import type { ExtensionValue } from '../interfaces/ExtensionValue';
4+
5+
type ExtensionConstructor = (ident: string, value: unknown) => ExtensionValue;
6+
7+
/**
8+
* Builder class for creating Config objects with a fluent interface.
9+
*
10+
* @example
11+
* ```typescript
12+
* const configBuilder = new ConfigBuilder(createExtensionFunction);
13+
* const config = configBuilder
14+
* .withDestination(destination)
15+
* .withProxyAddress(proxyAddress)
16+
* .withAuthToken(authToken)
17+
* ...
18+
* .build();
19+
* ```
20+
*/
21+
export class ConfigBuilder {
22+
private extensionConstructor: ExtensionConstructor;
23+
24+
private username: string = '';
25+
private password: string = '';
26+
private destination: string = '';
27+
private proxyAddress: string = '';
28+
private serverDomain: string = '';
29+
private authToken: string = '';
30+
private desktopSize?: DesktopSize;
31+
32+
private extensions: ExtensionValue[] = [];
33+
34+
/**
35+
* Creates a new ConfigBuilder instance.
36+
*
37+
* @param extensionConstructor - Function that creates extension values from identifiers and values.
38+
*/
39+
constructor(extensionConstructor: ExtensionConstructor) {
40+
this.extensionConstructor = extensionConstructor;
41+
}
42+
43+
/**
44+
* Optional parameter
45+
*
46+
* @param username - The username to use for authentication
47+
* @returns The builder instance for method chaining
48+
*/
49+
withUsername(username: string): ConfigBuilder {
50+
this.username = username;
51+
return this;
52+
}
53+
54+
/**
55+
* Optional parameter
56+
*
57+
* @param password - The password for authentication
58+
* @returns The builder instance for method chaining
59+
*/
60+
withPassword(password: string): ConfigBuilder {
61+
this.password = password;
62+
return this;
63+
}
64+
65+
/**
66+
* Required parameter
67+
*
68+
* @param destination - The destination address to connect to
69+
* @returns The builder instance for method chaining
70+
*/
71+
withDestination(destination: string): ConfigBuilder {
72+
this.destination = destination;
73+
return this;
74+
}
75+
76+
/**
77+
* Required parameter
78+
*
79+
* @param proxyAddress - The address of the proxy server
80+
* @returns The builder instance for method chaining
81+
*/
82+
withProxyAddress(proxyAddress: string): ConfigBuilder {
83+
this.proxyAddress = proxyAddress;
84+
return this;
85+
}
86+
87+
/**
88+
* Optional parameter
89+
*
90+
* @param serverDomain - The server domain to connect to
91+
* @returns The builder instance for method chaining
92+
*/
93+
withServerDomain(serverDomain: string): ConfigBuilder {
94+
this.serverDomain = serverDomain;
95+
return this;
96+
}
97+
98+
/**
99+
* Required parameter
100+
*
101+
* @param authToken - JWT token to connect to the proxy
102+
* @returns The builder instance for method chaining
103+
*/
104+
withAuthToken(authToken: string): ConfigBuilder {
105+
this.authToken = authToken;
106+
return this;
107+
}
108+
109+
/**
110+
* Optional parameter
111+
*
112+
* @param ident - The identifier for the extension
113+
* @param value - The value for the extension
114+
* @returns The builder instance for method chaining
115+
*/
116+
withExtension(ident: string, value: unknown): ConfigBuilder {
117+
this.extensions.push(this.extensionConstructor(ident, value));
118+
return this;
119+
}
120+
121+
/**
122+
* Optional
123+
*
124+
* @param desktopSize - The desktop size configuration object
125+
* @returns The builder instance for method chaining
126+
*/
127+
withDesktopSize(desktopSize: DesktopSize): ConfigBuilder {
128+
this.desktopSize = desktopSize;
129+
return this;
130+
}
131+
132+
/**
133+
* Builds a new Config instance.
134+
*
135+
* @throws {Error} If required parameters (destination, proxyAddress, authToken) are not set
136+
* @returns A new Config instance with the configured values
137+
*/
138+
build(): Config {
139+
if (this.destination === '') {
140+
throw new Error('destination has to be specified');
141+
}
142+
if (this.proxyAddress === '') {
143+
throw new Error('proxy address has to be specified');
144+
}
145+
if (this.authToken === '') {
146+
throw new Error('authentication token has to be specified');
147+
}
148+
const userData = { username: this.username, password: this.password };
149+
const proxyData = { address: this.proxyAddress, authToken: this.authToken };
150+
151+
const configOptions = {
152+
destination: this.destination,
153+
serverDomain: this.serverDomain,
154+
extensions: this.extensions,
155+
desktopSize: this.desktopSize,
156+
};
157+
158+
return new Config(userData, proxyData, configOptions);
159+
}
160+
}

web-client/iron-remote-desktop/src/services/PublicAPI.ts

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { SpecialCombination } from '../enums/SpecialCombination';
44
import { RemoteDesktopService } from './remote-desktop.service';
55
import type { UserInteraction } from '../interfaces/UserInteraction';
66
import type { ScreenScale } from '../enums/ScreenScale';
7-
import type { DesktopSize } from '../interfaces/DesktopSize';
7+
import { ConfigBuilder } from './ConfigBuilder';
8+
import { Config } from './Config';
89

910
export class PublicAPI {
1011
private remoteDesktopService: RemoteDesktopService;
@@ -13,31 +14,13 @@ export class PublicAPI {
1314
this.remoteDesktopService = remoteDesktopService;
1415
}
1516

16-
private connect(
17-
username: string,
18-
password: string,
19-
destination: string,
20-
proxyAddress: string,
21-
serverDomain: string,
22-
authToken: string,
23-
desktopSize?: DesktopSize,
24-
preConnectionBlob?: string,
25-
kdc_proxy_url?: string,
26-
use_display_control = false,
27-
): Promise<NewSessionInfo> {
17+
private configBuilder(): ConfigBuilder {
18+
return this.remoteDesktopService.configBuilder();
19+
}
20+
21+
private connect(config: Config): Promise<NewSessionInfo> {
2822
loggingService.info('Initializing connection.');
29-
const resultObservable = this.remoteDesktopService.connect(
30-
username,
31-
password,
32-
destination,
33-
proxyAddress,
34-
serverDomain,
35-
authToken,
36-
desktopSize,
37-
preConnectionBlob,
38-
kdc_proxy_url,
39-
use_display_control,
40-
);
23+
const resultObservable = this.remoteDesktopService.connect(config);
4124

4225
return resultObservable.toPromise();
4326
}
@@ -82,6 +65,7 @@ export class PublicAPI {
8265
getExposedFunctions(): UserInteraction {
8366
return {
8467
setVisibility: this.setVisibility.bind(this),
68+
configBuilder: this.configBuilder.bind(this),
8569
connect: this.connect.bind(this),
8670
setScale: this.setScale.bind(this),
8771
onSessionEvent: (callback) => {

0 commit comments

Comments
 (0)