Skip to content

Commit 3dec6fa

Browse files
authored
Merge branch 'master' into quarkus-websocket
2 parents c8d25c6 + 3ba15fb commit 3dec6fa

File tree

16 files changed

+646
-56
lines changed

16 files changed

+646
-56
lines changed

CONTRIBUTING.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ We use [All Contributors](https://allcontributors.org/docs/en/specification) spe
1818

1919
## Few tips for effective contributions
2020

21-
1. Understanding how the Generator works is key to making valuable contributions. You can start by actively following along and executing the steps given in the [generator template tutorial](https://www.asyncapi.com/docs/tools/generator/generator-template). Hands-on practice will provide you with a comprehensive understanding of how the Generator processes templates and allow you to experiment with customization.
22-
2. **PATIENCE** is crucial. Focus on creating meaningful contributions rather than rushing to submit a PR that adds little or no value to the project.
23-
3. Adding entirely new features might be challenging because most features are already well-established. You may need to spend more time understanding the repository to identify areas for improvement.
24-
4. You should research on Google regarding the @asyncapi/generator and related repositories (e.g., @asyncapi/parser or any of the template repositories) to identify potential improvements. AI can be a helpful assistant, but always double-check the information it provides with Google or StackOverflow.
25-
5. Follow **Issue First, PR Later** - Always check if there’s an existing issue before creating a new one, and raise your PR once the issue is confirmed/approved by any of the maintainers.
26-
6. Keep **PRs small and focused** – A small PR with a well-defined scope is easier to review and merge. Avoid bundling multiple changes into one PR.
27-
7. Collaborate with Other Contributors – If someone else has already raised an issue and you are interested in contributing to it, please communicate with them and collaborate instead of raising a separate PR independently. Working together leads to better contributions and avoids duplication of efforts. Open source is driven by **collaboration, not competition**.
21+
1. **Join the AsyncAPI Slack Workspace** - Connect with maintainers and fellow contributors in the `#generator` channel on [AsyncAPI Slack](https://www.asyncapi.com/slack-invite) to ask questions, share ideas, and get help. Also, consider attending the **weekly Knowledge Sharing meetings held every Wednesday**; details are shared in the channel.
22+
2. Understanding how the Generator works is key to making valuable contributions. You can start by actively following along and executing the steps given in the [generator template tutorial](https://www.asyncapi.com/docs/tools/generator/generator-template). Hands-on practice will provide you with a comprehensive understanding of how the Generator processes templates and allow you to experiment with customization.
23+
3. **PATIENCE** is crucial. Focus on creating meaningful contributions rather than rushing to submit a PR that adds little or no value to the project.
24+
4. Adding entirely new features might be challenging because most features are already well-established. You may need to spend more time understanding the repository to identify areas for improvement.
25+
5. You should research on Google regarding the @asyncapi/generator and related repositories (e.g., @asyncapi/parser or any of the template repositories) to identify potential improvements. AI can be a helpful assistant, but always double-check the information it provides with Google or StackOverflow.
26+
6. Follow **Issue First, PR Later** - Always check if there’s an existing issue before creating a new one, and raise your PR once the issue is confirmed/approved by any of the maintainers.
27+
7. Keep **PRs small and focused** – A small PR with a well-defined scope is easier to review and merge. Avoid bundling multiple changes into one PR.
28+
8. Collaborate with Other Contributors – If someone else has already raised an issue and you are interested in contributing to it, please communicate with them and collaborate instead of raising a separate PR independently. Working together leads to better contributions and avoids duplication of efforts. Open source is driven by **collaboration, not competition**.
2829

2930
## Summary of the contribution flow
3031

apps/generator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"test:unit": "jest --coverage --testPathIgnorePatterns=integration --testPathIgnorePatterns=test-project",
1717
"test:dev": "npm run test:unit -- --watchAll",
1818
"test:integration": "npm run test:cleanup && jest --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
19-
"test:integration:update": "jest --updateSnapshot --testPathPattern=integration --modulePathIgnorePatterns='./__mocks__(?!\\/loglevel\\.js$)'",
19+
"test:integration:update": "npm run test:integration -- -u",
2020
"test:cleanup": "rimraf \"test/temp\"",
2121
"docs": "jsdoc2md --partial docs/jsdoc2md-handlebars/custom-sig-name.hbs docs/jsdoc2md-handlebars/main.hbs docs/jsdoc2md-handlebars/docs.hbs docs/jsdoc2md-handlebars/header.hbs docs/jsdoc2md-handlebars/defaultvalue.hbs docs/jsdoc2md-handlebars/link.hbs docs/jsdoc2md-handlebars/params-table.hbs --files lib/generator.js > docs/api.md",
2222
"docker:build": "docker build -t asyncapi/generator:latest .",

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"helpers:lint": "turbo run lint --filter=@asyncapi/generator-helpers",
3232
"templates:test": "turbo run test --filter=@asyncapi/template*",
3333
"packages:test": "turbo run test --filter=./packages/** --only",
34-
"hooks:test": "turbo run test --filter=@asyncapi/generator-hooks"
34+
"hooks:test": "turbo run test --filter=@asyncapi/generator-hooks",
35+
"test:update": "turbo run test -- -u"
3536
},
3637
"devDependencies": {
3738
"markdown-toc": "^1.2.0",

packages/components/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Package with reusable components for generation using React render engine",
55
"scripts": {
66
"test": "npm run build && jest --coverage",
7-
"test:update": "jest --updateSnapshot",
7+
"test:update": "npm run test -- -u",
88
"build": "babel src --out-dir lib",
99
"prepublishOnly": "npm run build",
1010
"lint": "eslint --max-warnings 0 --config ../../.eslintrc --ignore-path ../../.eslintignore .",

packages/helpers/src/bindings.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ function getQueryParams(channels) {
1818

1919
const queryMap = new Map();
2020

21-
const hasWsBinding = channel?.bindings?.().has('ws');
21+
const bindings = channel?.bindings?.();
22+
const hasWsBinding = bindings?.has('ws');
2223

2324
if (!hasWsBinding) {
2425
return null;
2526
}
2627

27-
const wsBinding = channel.bindings().get('ws');
28-
28+
const wsBinding = bindings.get('ws');
2929
const query = wsBinding.value()?.query;
3030
//we do not throw error, as user do not have to use query params, we just exit with null as it doesn't make sense to continue with query building
3131
if (!query) {

packages/helpers/test/__fixtures__/asyncapi-websocket-query.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ channels:
7474
payload: false
7575
- name: number
7676
payload: 123
77+
wsBindingNoQuery:
78+
address: '/no-query'
79+
bindings:
80+
ws:
81+
bindingVersion: 0.1.0
82+
wsBindingEmptyQuery:
83+
address: '/empty-query'
84+
bindings:
85+
ws:
86+
bindingVersion: 0.1.0
87+
query:
88+
type: object
89+
7790
emptyChannel: {}
7891

7992
marketDataV1NoBinding:
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
const path = require('path');
2+
const { Parser, fromFile } = require('@asyncapi/parser');
3+
const { getQueryParams } = require('@asyncapi/generator-helpers');
4+
const parser = new Parser();
5+
const asyncapi_v3_path = path.resolve(__dirname, './__fixtures__/asyncapi-websocket-query.yml');
6+
7+
function createChannelsWithOnly(keysToKeep, originalChannels) {
8+
// Create a copy to avoid mutating the original channels map -
9+
// other tests need the original data intact
10+
const channelsMap = new Map(originalChannels.all());
11+
for (const key of channelsMap.keys()) {
12+
if (!keysToKeep.includes(key)) {
13+
channelsMap.delete(key);
14+
}
15+
}
16+
return channelsMap;
17+
}
18+
19+
describe('getQueryParams integration test with AsyncAPI', () => {
20+
let parsedAsyncAPIDocument;
21+
22+
beforeAll(async () => {
23+
const parseResult = await fromFile(parser, asyncapi_v3_path).parse();
24+
parsedAsyncAPIDocument = parseResult.document;
25+
});
26+
27+
// Helper function to create filtered channels and get query params
28+
const getQueryParamsForChannels = (channelNames) => {
29+
const channels = parsedAsyncAPIDocument.channels();
30+
const filteredChannelsMap = createChannelsWithOnly(channelNames, channels);
31+
// Mock the channels interface that getQueryParams() expects - it needs
32+
// isEmpty() and all() methods, not direct Map access
33+
return getQueryParams({
34+
isEmpty: () => filteredChannelsMap.size === 0,
35+
all: () => filteredChannelsMap
36+
});
37+
};
38+
39+
it('should extract query parameters from WebSocket binding with properties', () => {
40+
const channels = parsedAsyncAPIDocument.channels();
41+
const params = getQueryParams(channels);
42+
43+
expect(params).not.toBeNull();
44+
expect(params.get('heartbeat')).toBe('false');
45+
expect(params.get('top_of_book')).toBe('false');
46+
expect(params.get('bids')).toBe('true');
47+
expect(params.get('offers')).toBe('');
48+
});
49+
50+
it('should return null for channel without WebSocket binding', () => {
51+
const params = getQueryParamsForChannels(['marketDataV1NoBinding']);
52+
expect(params).toBeNull();
53+
});
54+
55+
it('should return null for empty channels map', () => {
56+
// Mock channels object to simulate empty state - getQueryParams() expects
57+
// channels with isEmpty() and all() methods, not a plain Map
58+
const emptyChannels = {
59+
isEmpty: () => true,
60+
all: () => new Map()
61+
};
62+
const params = getQueryParams(emptyChannels);
63+
expect(params).toBeNull();
64+
});
65+
66+
it('should return null for channel with empty binding', () => {
67+
const params = getQueryParamsForChannels(['emptyChannel']);
68+
expect(params).toBeNull();
69+
});
70+
71+
it('should return null if WebSocket binding exists but has no query parameters', () => {
72+
const params = getQueryParamsForChannels(['wsBindingNoQuery']);
73+
expect(params).toBeNull();
74+
});
75+
76+
it('should return null if WebSocket binding query exists but has no properties', () => {
77+
const params = getQueryParamsForChannels(['wsBindingEmptyQuery']);
78+
expect(params).toBeNull();
79+
});
80+
});

packages/helpers/test/utils.test.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const path = require('path');
22
const { Parser, fromFile } = require('@asyncapi/parser');
3-
const { getClientName, getInfo, getTitle, toSnakeCase } = require('@asyncapi/generator-helpers');
3+
const { getClientName, getInfo, getTitle, toSnakeCase, listFiles } = require('@asyncapi/generator-helpers');
4+
const fs = require('fs/promises');
45

56
const parser = new Parser();
67
const asyncapi_v3_path = path.resolve(__dirname, './__fixtures__/asyncapi-websocket-query.yml');
@@ -153,4 +154,35 @@ describe('toSnakeCase integration test with AsyncAPI', () => {
153154
const expectedOperationId = '';
154155
expect(actualOperationId).toBe(expectedOperationId);
155156
});
156-
});
157+
});
158+
159+
jest.mock('fs/promises');
160+
describe('listFiles', () => {
161+
afterEach(() => {
162+
jest.clearAllMocks();
163+
});
164+
165+
it('should return only file names from the directory', async () => {
166+
const mockDirents = [
167+
{ name: 'file1.txt', isFile: () => true },
168+
{ name: 'file2.js', isFile: () => true },
169+
{ name: 'subdir', isFile: () => false },
170+
];
171+
172+
fs.readdir.mockResolvedValue(mockDirents);
173+
const mockPath = '/mock/path';
174+
175+
const result = await listFiles(mockPath);
176+
expect(fs.readdir).toHaveBeenCalledWith(mockPath, { withFileTypes: true });
177+
expect(result).toEqual(['file1.txt', 'file2.js']);
178+
});
179+
180+
it('should return an empty array if no files exist', async () => {
181+
fs.readdir.mockResolvedValue([
182+
{ name: 'folder', isFile: () => false },
183+
]);
184+
const mockPath = '/mock/path';
185+
const result = await listFiles(mockPath);
186+
expect(result).toEqual([]);
187+
});
188+
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "This is a template generating Dart websocket client",
55
"scripts": {
66
"test": "npm run test:cleanup && jest --coverage",
7-
"test:update": "jest --updateSnapshot",
7+
"test:update": "npm run test -- -u",
88
"test:cleanup": "rimraf \"test/temp\"",
99
"lint": "eslint --max-warnings 0 --config ../../../../../.eslintrc --ignore-path ../../../../../.eslintignore .",
1010
"lint:fix": "eslint --fix --max-warnings 0 --config ../../../../../.eslintrc --ignore-path ../../../../../.eslintignore ."

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 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.
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/client.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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const WSClient = require('./test/temp/snapshotTestResult/custom-client-hoppscotch.js');
1+
const WSClient = require('./test/temp/snapshotTestResult/custom_client_hoppscotch/client.js');
22
// Example usage
33
const wsClient = new WSClient();
44

0 commit comments

Comments
 (0)