Skip to content

Commit 20d10dc

Browse files
Adi-204derberg
andauthored
refactor: improve getClientName helper (#1513)
Co-authored-by: Adi-204 <[email protected]> Co-authored-by: Lukasz Gornicki <[email protected]>
1 parent 0425b38 commit 20d10dc

File tree

20 files changed

+556
-45
lines changed

20 files changed

+556
-45
lines changed

packages/helpers/src/utils.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
/**
2-
* Get client name from AsyncAPI info.title
2+
* Get client name from AsyncAPI info.title or uses a custom name if provided.
33
*
44
* @param {object} info - The AsyncAPI info object.
5+
* @param {boolean} appendClientSuffix - Whether to append "Client" to the generated name
6+
* @param {string} [customClientName] - Optional custom client name to use instead of generating from title
57
*
6-
* return {string} - The client name with "Client" appended at the end.
8+
* @returns {string} The formatted client name, either the custom name or a generated name based on the title
79
*/
8-
module.exports.getClientName = (info) => {
10+
module.exports.getClientName = (info, appendClientSuffix, customClientName) => {
11+
if (customClientName) {
12+
return customClientName;
13+
}
914
const title = info.title();
10-
11-
// Remove spaces, make the first letter uppercase, and add "Client" at the end
12-
return `${title.replace(/\s+/g, '') // Remove all spaces
13-
.replace(/^./, char => char.toUpperCase()) // Make the first letter uppercase
14-
}Client`;
15+
const baseName = `${title.replace(/\s+/g, '') // Remove all spaces
16+
.replace(/^./, char => char.toUpperCase())}`; // Make the first letter uppercase
17+
return appendClientSuffix ? `${baseName}Client` : baseName;
1518
};
1619

packages/helpers/test/utils.test.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,36 @@ describe('getClientName integration test with AsyncAPI', () => {
1313
parsedAsyncAPIDocument = parseResult.document;
1414
});
1515

16-
it('should generate correct client name for the provided AsyncAPI info object', () => {
16+
it('should generate correct client name for the provided AsyncAPI info object without appendClientSuffix', () => {
1717
const info = parsedAsyncAPIDocument.info();
18+
const appendClientSuffix = false;
19+
const customClientName = '';
1820

19-
const clientName = getClientName(info);
21+
const clientName = getClientName(info, appendClientSuffix, customClientName);
22+
23+
// Example assertion: Check if the name is formatted correctly
24+
expect(clientName).toBe('GeminiMarketDataWebsocketAPI');
25+
});
26+
27+
it('should generate correct client name for the provided AsyncAPI info object with appendClientSuffix', () => {
28+
const info = parsedAsyncAPIDocument.info();
29+
const appendClientSuffix = true;
30+
const customClientName = '';
31+
32+
const clientName = getClientName(info, appendClientSuffix, customClientName);
2033

2134
// Example assertion: Check if the name is formatted correctly
2235
expect(clientName).toBe('GeminiMarketDataWebsocketAPIClient');
2336
});
37+
38+
it('should return customClientName', () => {
39+
const info = parsedAsyncAPIDocument.info();
40+
const appendClientSuffix = false;
41+
const customClientName = 'GeminiClient';
42+
43+
const clientName = getClientName(info, appendClientSuffix, customClientName);
44+
45+
// Example assertion: Check if the name is formatted correctly
46+
expect(clientName).toBe(customClientName);
47+
});
2448
});

packages/templates/clients/websocket/dart/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ You can test this template:
44
1. Clone the project and run `npm install`
55
2. Navigate to `packages/templates/clients/websocket/dart`
66
3. Install with `npm install` and run test with `npm run test`
7-
4. Navigate to the generated Postman client folder with `cd test/temp/snapshotTestResult/client_postman`
7+
4. Navigate to the generated Hoppscotch client folder with `cd test/temp/snapshotTestResult/custom_client_hoppscotch`
88
5. Install dependencies of the generated client: `dart pub get`
99
6. Start an example script that uses a client library generated by the test: `dart run --packages=.dart_tool/package_config.json ../../../../example.dart`
1010

11-
> By default, this is testing against Postman Echo service. You can modify `packages/templates/clients/websocket/dart/example.dart` and change first line to `import 'test/temp/snapshotTestResult/client_hoppscotch/client.dart';` and line 15 `final wsClient = HoppscotchEchoWebSocketClient();` and run `dart run --packages=.dart_tool/package_config.json ../../../../example.dart` again. You will see the example still works but against a different API. This is possible as both AsyncAPI documents have the same name of operation for sending messages: `sendEchoMessage` so each client generated has the same API.
11+
> By default, this is testing against Hoppscotch Echo service. You can modify `packages/templates/clients/websocket/dart/example.dart` and change first line to `import 'test/temp/snapshotTestResult/client_postman/client.dart';` and line 15 `final wsClient = PostmanEchoWebSocketClientClient();` and run `dart run --packages=.dart_tool/package_config.json ../../../../example.dart` again. You will see the example still works but against a different API. This is possible as both AsyncAPI documents have the same name of operation for sending messages: `sendEchoMessage` so each client generated has the same API.

packages/templates/clients/websocket/dart/example.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'dart:async';
2-
import 'test/temp/snapshotTestResult/client_postman/client.dart';
2+
import 'test/temp/snapshotTestResult/custom_client_hoppscotch/client.dart';
33

44
void myHandler(String message) {
55
print('====================');
@@ -12,7 +12,7 @@ void myErrorHandler(Object error) {
1212
}
1313

1414
Future<void> main() async {
15-
final wsClient = PostmanEchoWebSocketClientClient();
15+
final wsClient = HoppscotchClient();
1616
wsClient.registerMessageHandler(myHandler);
1717
wsClient.registerErrorHandler(myErrorHandler);
1818

packages/templates/clients/websocket/dart/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@
6767
"description": "The name of the generated client file",
6868
"required": false,
6969
"default": "client.dart"
70+
},
71+
"appendClientSuffix": {
72+
"description": "Add 'Client' suffix at the end of the class name. This option has no effect if 'customClientName' is specified.",
73+
"required": false,
74+
"default": false
75+
},
76+
"customClientName": {
77+
"description": "The custom name for the generated client class",
78+
"required": false
7079
}
7180
}
7281
}

packages/templates/clients/websocket/dart/template/client.dart.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function ({ asyncapi, params }) {
88
const server = getServer(asyncapi.servers(), params.server);
99
const info = asyncapi.info();
1010
const title = info.title();
11-
const clientName = getClientName(info);
11+
const clientName = getClientName(info, params.appendClientSuffix, params.customClientName);
1212
const serverUrl = getServerUrl(server);
1313
return (
1414
// The clientFileName default values can be found and modified under the package.json

packages/templates/clients/websocket/dart/test/__snapshots__/integration.test.js.snap

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,119 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`testing if generated client match snapshot generate simple client for hoppscotch echo with custom client name: client.dart 1`] = `
4+
"//////////////////////////////////////////////////////////////////////
5+
///
6+
/// Hoppscotch Echo WebSocket Client - 1.0.0
7+
/// Protocol: wss
8+
/// Host: echo-websocket.hoppscotch.io
9+
///
10+
//////////////////////////////////////////////////////////////////////
11+
12+
import 'dart:convert';
13+
import 'package:web_socket_channel/web_socket_channel.dart';
14+
15+
class HoppscotchClient {
16+
17+
final String _url;
18+
WebSocketChannel? _channel;
19+
final List<void Function(String)> _messageHandlers = [];
20+
final List<void Function(Object)> _errorHandlers = [];
21+
22+
/// Constructor to initialize the WebSocket client
23+
///
24+
/// [url] - The WebSocket server URL. Use it if the server URL is different from the default one taken from the AsyncAPI document.
25+
HoppscotchClient({String? url})
26+
: _url = url ?? 'wss://echo-websocket.hoppscotch.io';
27+
28+
29+
/// Method to establish a WebSocket connection
30+
Future<void> connect() async {
31+
if (_channel != null) {
32+
print('Already connected to Hoppscotch Echo WebSocket Client server');
33+
return;
34+
}
35+
try {
36+
final wsUrl = Uri.parse(_url);
37+
_channel = WebSocketChannel.connect(wsUrl);
38+
print('Connected to Hoppscotch Echo WebSocket Client server');
39+
40+
/// Listen to the incoming message stream
41+
_channel?.stream.listen(
42+
(message) {
43+
if (_messageHandlers.isNotEmpty) {
44+
for (var handler in _messageHandlers) {
45+
_handleMessage(message, handler);
46+
}
47+
} else {
48+
print('Message received: $message');
49+
}
50+
},
51+
onError: (error) {
52+
if (_errorHandlers.isNotEmpty) {
53+
for (var handler in _errorHandlers) {
54+
handler(error);
55+
}
56+
} else {
57+
print('WebSocket Error: $error');
58+
}
59+
},
60+
onDone: () {
61+
_channel = null;
62+
print('Disconnected from Hoppscotch Echo WebSocket Client server');
63+
},
64+
);
65+
} catch (error) {
66+
print('Connection failed: $error');
67+
rethrow;
68+
}
69+
}
70+
71+
/// Method to register custom message handlers
72+
void registerMessageHandler(void Function(String) handler) {
73+
_messageHandlers.add(handler);
74+
}
75+
76+
/// Method to register custom error handlers
77+
void registerErrorHandler(void Function(Object) handler) {
78+
_errorHandlers.add(handler);
79+
}
80+
81+
/// Method to handle message with callback
82+
void _handleMessage(dynamic message, void Function(String) cb) {
83+
cb(message is String ? message : message.toString());
84+
}
85+
86+
/// Method to send an echo message to the server
87+
void sendEchoMessage(dynamic message) {
88+
if (_channel == null) {
89+
print('Error: WebSocket is not connected.');
90+
return;
91+
}
92+
final payload = message is String ? message : jsonEncode(message);
93+
_channel!.sink.add(payload);
94+
print('Sent message to echo server: $payload');
95+
}
96+
97+
/// Method to close the WebSocket connection
98+
void close() {
99+
_channel?.sink.close();
100+
_channel = null;
101+
print('WebSocket connection closed.');
102+
}
103+
}
104+
105+
"
106+
`;
107+
108+
exports[`testing if generated client match snapshot generate simple client for hoppscotch echo with custom client name: pubspec.yaml 1`] = `
109+
"name: wsclient
110+
environment:
111+
sdk: '>=3.0.0 <4.0.0'
112+
dependencies:
113+
web_socket_channel: ^3.0.2
114+
"
115+
`;
116+
3117
exports[`testing if generated client match snapshot generate simple client for hoppscotch echo: client.dart 1`] = `
4118
"//////////////////////////////////////////////////////////////////////
5119
///
@@ -12,7 +126,7 @@ exports[`testing if generated client match snapshot generate simple client for h
12126
import 'dart:convert';
13127
import 'package:web_socket_channel/web_socket_channel.dart';
14128
15-
class HoppscotchEchoWebSocketClientClient {
129+
class HoppscotchEchoWebSocketClient {
16130
17131
final String _url;
18132
WebSocketChannel? _channel;
@@ -22,7 +136,7 @@ class HoppscotchEchoWebSocketClientClient {
22136
/// Constructor to initialize the WebSocket client
23137
///
24138
/// [url] - The WebSocket server URL. Use it if the server URL is different from the default one taken from the AsyncAPI document.
25-
HoppscotchEchoWebSocketClientClient({String? url})
139+
HoppscotchEchoWebSocketClient({String? url})
26140
: _url = url ?? 'wss://echo-websocket.hoppscotch.io';
27141
28142

packages/templates/clients/websocket/dart/test/integration.test.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ const asyncapi_v3_path_hoppscotch = path.resolve(__dirname, '../../test/__fixtur
1010
const testResultPath = path.resolve(__dirname, './temp/snapshotTestResult');
1111
const testResultPathPostman = path.join(testResultPath, 'client_postman');
1212
const testResultPathHoppscotch = path.join(testResultPath, 'client_hoppscotch');
13+
const testResultPathCustomHoppscotch = path.join(testResultPath, 'custom_client_hoppscotch');
1314
const template = path.resolve(__dirname, '../');
1415

16+
const testOutputFiles = ['client.dart', 'pubspec.yaml'];
17+
1518
describe('testing if generated client match snapshot', () => {
1619
jest.setTimeout(100000);
17-
18-
it('generate simple client for postman echo', async () => {
19-
const testOutputFiles = ['client.dart', 'pubspec.yaml'];
2020

21+
it('generate simple client for postman echo', async () => {
2122
const generator = new Generator(template, testResultPathPostman, {
2223
forceWrite: true,
2324
templateParams: {
2425
server: 'echoServer',
25-
clientFileName: testOutputFiles[0]
26+
clientFileName: testOutputFiles[0],
27+
appendClientSuffix: true
2628
}
2729
});
2830

@@ -37,8 +39,6 @@ describe('testing if generated client match snapshot', () => {
3739
});
3840

3941
it('generate simple client for hoppscotch echo', async () => {
40-
const testOutputFiles = ['client.dart', 'pubspec.yaml'];
41-
4242
const generator = new Generator(template, testResultPathHoppscotch, {
4343
forceWrite: true,
4444
templateParams: {
@@ -56,4 +56,23 @@ describe('testing if generated client match snapshot', () => {
5656
expect(content).toMatchSnapshot(testOutputFile);
5757
}
5858
});
59+
60+
it('generate simple client for hoppscotch echo with custom client name', async () => {
61+
const generator = new Generator(template, testResultPathCustomHoppscotch, {
62+
forceWrite: true,
63+
templateParams: {
64+
server: 'echoServer',
65+
clientFileName: testOutputFiles[0],
66+
customClientName: 'HoppscotchClient'
67+
}
68+
});
69+
70+
await generator.generateFromFile(asyncapi_v3_path_hoppscotch);
71+
72+
for (const testOutputFile of testOutputFiles) {
73+
const filePath = path.join(testResultPathCustomHoppscotch, testOutputFile);
74+
const content = await readFile(filePath, 'utf8');
75+
expect(content).toMatchSnapshot(testOutputFile);
76+
}
77+
});
5978
});

packages/templates/clients/websocket/javascript/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ You can test this template:
66
3. Install with `npm install` and run test with `npm run test`
77
4. Start example script that uses a client library generated by the test: `node example.js`
88

9-
> By default this is testing against Postman echo service. You can modify `packages/templates/clients/websocket/javascript/example.js` and change first line to `const WSClient = require('./tests/temp/snapshotTestResult/client-hoppscotch.js');` and run `node example.js` again. You will see example still works but now it is using a different API. This is possible since both AsyncAPI documents define the same name of operation for sending messages: `sendEchoMessage` so each client generated has the same API.
9+
> By default this is testing against Hoppscotch echo service. You can modify `packages/templates/clients/websocket/javascript/example.js` and change first line to `const WSClient = require('./tests/temp/snapshotTestResult/client-postman.js');` and run `node example.js` again. You will see example still works but now it is using a different API. This is possible since both AsyncAPI documents define the same name of operation for sending messages: `sendEchoMessage` so each client generated has the same API.

packages/templates/clients/websocket/javascript/example.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const WSClient = require('./test/temp/snapshotTestResult/client-postman');
1+
const WSClient = require('./test/temp/snapshotTestResult/custom-client-hoppscotch.js');
22
// Example usage
33
const wsClient = new WSClient();
44

@@ -27,7 +27,7 @@ async function main() {
2727

2828
while (true) {
2929
try {
30-
await wsClient.sendEchoMessage(message);
30+
await WSClient.sendEchoMessage(message, wsClient.websocket);
3131
} catch (error) {
3232
console.error('Error while sending message:', error);
3333
}

packages/templates/clients/websocket/javascript/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@
6767
"description": "The name of the generated client file",
6868
"required": false,
6969
"default": "client.js"
70+
},
71+
"appendClientSuffix": {
72+
"description": "Add 'Client' suffix at the end of the class name. This option has no effect if 'customClientName' is specified.",
73+
"required": false,
74+
"default": false
75+
},
76+
"customClientName": {
77+
"description": "The custom name for the generated client class",
78+
"required": false
7079
}
7180
}
7281
}

packages/templates/clients/websocket/javascript/template/client.js.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function ({ asyncapi, params }) {
88
const server = getServer(asyncapi.servers(), params.server);
99
const info = asyncapi.info();
1010
const title = info.title();
11-
const clientName = getClientName(info);
11+
const clientName = getClientName(info, params.appendClientSuffix, params.customClientName);
1212
const serverUrl = getServerUrl(server);
1313
return (
1414
<File name={params.clientFileName}>

0 commit comments

Comments
 (0)