Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/components/src/components/Connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { OnOpen } from './OnOpen';
import { OnMessage } from './OnMessage';
import { OnError } from './OnError';
import { OnClose } from './OnClose';
import { unsupportedLanguage } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart'} Language
Expand Down Expand Up @@ -98,6 +99,7 @@ Future<void> connect() async {
* @param {Language} props.language - The programming language for which to generate connection code.
* @param {string} props.title - The title of the WebSocket server.
* @return {JSX.Element} A Text component containing the generated WebSocket connection code for the specified language.
* @throws When the specified language is not supported.
*
* @example
* const language = "python";
Expand All @@ -115,13 +117,18 @@ Future<void> connect() async {
* renderConnect();
*/
export function Connect({ language, title }) {
const onOpenMethod = render(<OnOpen language={language} title={title} />);
const supportedLanguages = Object.keys(websocketConnectMethod);
const generateConnectCode = websocketConnectMethod[language];

if (!generateConnectCode) {
throw unsupportedLanguage(language, supportedLanguages);
}

const onOpenMethod = language === 'dart' ? '' : render(<OnOpen language={language} title={title} />);
const onMessageMethod = render(<OnMessage language={language} />);
const onErrorMethod = render(<OnError language={language} />);
const onCloseMethod = render(<OnClose language={language} title={title} />);

const generateConnectCode = websocketConnectMethod[language];

let connectMethod;

if (language === 'dart') {
Expand Down
46 changes: 33 additions & 13 deletions packages/components/src/components/DependencyProvider.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable sonarjs/no-duplicate-string */
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedFramework, unsupportedLanguage, invalidRole } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart' | 'java'} Language
Expand Down Expand Up @@ -57,6 +58,28 @@ const dependenciesConfig = {
}
};

/**
* Helper function to resolve dependencies for framework and role configurations.
*
* @private
* @param {Object} frameworkConfig - The framework configuration object.
* @param {string} role - The role (e.g., 'client', 'connector' for Java).
* @returns {string[]} Array of dependency strings or empty array.
* @throws {Error} If the role is not supported by the framework configuration.
*/
function resolveFrameworkDependencies(frameworkConfig, role) {
if (!role) {
return frameworkConfig.dependencies || [];
}

const supportedRoles = Object.keys(frameworkConfig);
if (!supportedRoles.includes(role)) {
throw invalidRole(role, supportedRoles);
}

return frameworkConfig[role]?.dependencies || frameworkConfig.dependencies || [];
}

/**
* Helper function to resolve dependencies based on language, framework, and role.
*
Expand All @@ -65,12 +88,15 @@ const dependenciesConfig = {
* @param {string} framework - The framework (e.g., 'quarkus' for Java).
* @param {string} role - The role (e.g., 'client', 'connector' for Java).
* @returns {string[]} Array of dependency strings.
* @throws {Error} When the specified language is not supported.
* @throws {Error} When the specified framework is not supported for the given language.
*/
function resolveDependencies(language, framework = '', role = '') {
const config = dependenciesConfig[language];
const supportedLanguages = Object.keys(dependenciesConfig);

if (!config) {
return [];
throw unsupportedLanguage(language, supportedLanguages);
}

// Handle flat structure (python, javascript, dart)
Expand All @@ -79,19 +105,13 @@ function resolveDependencies(language, framework = '', role = '') {
}

// Handle nested structure (java with quarkus framework and roles)
if (framework && config[framework]) {
const frameworkConfig = config[framework];

if (role && frameworkConfig[role] && frameworkConfig[role].dependencies) {
return frameworkConfig[role].dependencies;
}

if (frameworkConfig.dependencies) {
return frameworkConfig.dependencies;
}
}
const supportedFrameworks = Object.keys(config);

return [];
if (!config[framework]) {
throw unsupportedFramework(language, framework, supportedFrameworks);
}

return resolveFrameworkDependencies(config[framework], role);
}

/**
Expand Down
23 changes: 19 additions & 4 deletions packages/components/src/components/FileHeaderInfo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedLanguage, invalidInfo, invalidServer } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'typescript' | 'java' | 'csharp' | 'rust' | 'dart'} Language
Expand Down Expand Up @@ -27,6 +28,9 @@ const commentConfig = {
* @param {Object} props.server - Server object from the AsyncAPI document.
* @param {Language} props.language - Programming language used for comment formatting.
* @returns {JSX.Element} A Text component that contains file header.
* @throws {Error} When info is missing or invalid.
* @throws {Error} When server is missing or invalid.
* @throws {Error} When the specified language is not supported.
*
* @example
* import path from "path";
Expand Down Expand Up @@ -54,10 +58,21 @@ const commentConfig = {
* renderFileHeader().catch(console.error);
*/
export function FileHeaderInfo({ info, server, language }) {
const { commentChar, lineStyle } = commentConfig[language] || {
commentChar: '//',
lineStyle: '//'.repeat(25)
};
if (!info) {
throw invalidInfo();
}

if (!server) {
throw invalidServer();
}

const supportedLanguages = Object.keys(commentConfig);

if (!supportedLanguages.includes(language)) {
throw unsupportedLanguage(language, supportedLanguages);
}

const { commentChar, lineStyle } = commentConfig[language];

return (
<Text>
Expand Down
30 changes: 27 additions & 3 deletions packages/components/src/components/MethodGenerator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedLanguage, negativeIndent, invalidMethodName, invalidNewLines, invalidMethodParams } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart' | 'java'} Language
Expand Down Expand Up @@ -75,18 +76,19 @@ const buildIndentedLogic = (logic, preExecutionCode, postExecutionCode, indentSi
*
* @param {Object} props - Component props.
* @param {Language} props.language - Programming language used for method formatting.
* @param {string} props.methodName - Name of the method.
* @param {string} props.methodName - Name of the method (non-empty string required).
* @param {string[]} [props.methodParams=[]] - Method parameters.
* @param {string} [props.methodDocs=''] - Optional documentation string.
* @param {string} [props.methodLogic=''] - Core method logic.
* @param {string} [props.preExecutionCode=''] - Code before main logic.
* @param {string} [props.postExecutionCode=''] - Code after main logic.
* @param {number} [props.indent=2] - Indentation for the method block.
* @param {number} [props.indent=2] - Indentation for the method block (must be >= 0).
* @param {number} [props.newLines=1] - Number of new lines after method.
* @param {{ returnType: string | undefined, openingTag: string | undefined, closingTag: string | undefined, indentSize: number | undefined, parameterWrap: boolean | undefined }} [props.customMethodConfig] - Optional custom syntax configuration for the current language.
* @param {{ returnType: string | undefined, openingTag: string | undefined, closingTag: string | undefined, indentSize: number | undefined, parameterWrap: boolean | undefined }} [props.customMethodConfig] - Optional custom syntax configuration for the current language.
* @param {Record<Language, { methodDocs: string | undefined, methodLogic: string | undefined } | Record<string, { methodDocs: string | undefined, methodLogic: string | undefined }>>} [props.methodConfig] - Language-level or framework-level configuration.
* @param {string} [props.framework] - Framework name for nested configurations (e.g., 'quarkus' for Java).
* @returns {JSX.Element} A Text component that contains method block with appropriate formatting.
* @throws {Error} If language is unsupported, methodName is invalid, or indent is negative.
*
* @example
* const language = "java";
Expand Down Expand Up @@ -133,6 +135,28 @@ export function MethodGenerator({
methodConfig,
framework
}) {
const supportedLanguages = Object.keys(defaultMethodConfig);

if (!supportedLanguages.includes(language)) {
throw unsupportedLanguage(language, supportedLanguages);
}

if (typeof methodName !== 'string') {
throw invalidMethodName();
}
Comment on lines +144 to +146
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

methodName validation is incomplete and loses error context.

Two issues:

  1. The error message from invalidMethodName says "Expected a non-empty string", but the check only rejects non-string types — an empty string "" passes through and would produce malformed output.
  2. invalidMethodName() is called without the actual methodName value, so the error message will always say "Received: undefined" regardless of what was actually passed.
Suggested fix
-  if (typeof methodName !== 'string') {
-    throw invalidMethodName();
+  if (typeof methodName !== 'string' || methodName.trim() === '') {
+    throw invalidMethodName(methodName);
   }
🤖 Prompt for AI Agents
In `@packages/components/src/components/MethodGenerator.js` around lines 144 -
146, The current validation for methodName only checks type and calls
invalidMethodName() without context; update the check in the function that uses
methodName to ensure it is a non-empty string (e.g., typeof methodName ===
'string' && methodName.trim().length > 0) and pass the actual methodName value
into invalidMethodName(methodName) so the thrown error includes the received
value and properly rejects empty strings; reference the methodName variable and
the invalidMethodName() factory in your change.

Copy link
Contributor Author

@batchu5 batchu5 Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Adi-204 I'm a little bit confused here, because in

<CloseConnection language="java" framework="quarkus" methodName="" indent={0} />

We are calling CloseConnection, which in turn calls the methodGenerator with an empty string as the methodName.

What do you suggest?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@batchu5 honestly I don't have much idea as the template was develop during last year GSoC project by other guy. It also depends what params for a component you are keeping as optional/required and logic inside the component.
A small advice from myside would be after refactoring you do, please make sure integration test snapshots are not changed too much as it means template is generated as older version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@batchu5 one imp thing remember you will have to look at how components are used in different templates and decide which params of component are required or optional. As you can't make every param as required becoz than it would fail for some templates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay @Adi-204

Copy link
Contributor Author

@batchu5 batchu5 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, I'll keep it as it is!


if (indent < 0) {
throw negativeIndent(indent);
}

if (newLines < 0) {
throw invalidNewLines(newLines);
}

if (!Array.isArray(methodParams)) {
throw invalidMethodParams(methodParams);
}

const { docs: resolvedMethodDocs, logic: resolvedMethodLogic } = resolveDocsAndLogic({
language,
methodDocs,
Expand Down
4 changes: 4 additions & 0 deletions packages/components/src/components/Models.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
FormatHelpers,
JavaScriptGenerator
} from '@asyncapi/modelina';
import { invalidAsyncAPI } from '../utils/ErrorHandling';

/**
* @typedef {'toPascalCase' | 'toCamelCase' | 'toKebabCase' | 'toSnakeCase'} Format
Expand Down Expand Up @@ -87,6 +88,9 @@ const formatHelpers = {
*
*/
export async function Models({ asyncapi, language = 'python', format = 'toPascalCase', presets, constraints }) {
if (!asyncapi) {
throw invalidAsyncAPI();
}
// Get the selected generator and file extension, defaulting to Python if unknown
const { generator: GeneratorClass, extension } = generatorConfig[language] || generatorConfig.python;

Expand Down
29 changes: 20 additions & 9 deletions packages/components/src/components/OnClose.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedFramework, unsupportedLanguage } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart' | 'java' } Language
Expand Down Expand Up @@ -63,6 +64,7 @@ const resolveCloseConfig = (language, framework = '') => {
if (framework && typeof config[framework] === 'function') {
return config[framework];
}
return null;
};

/**
Expand All @@ -74,6 +76,8 @@ const resolveCloseConfig = (language, framework = '') => {
* @param {string} props.title - The title of the WebSocket server.
*
* @returns {JSX.Element} A Text component containing the onClose handler code for the specified language.
* @throws {Error} When the specified language is not supported.
* @throws {Error} When the specified framework is not supported for the given language.
*
* @example
* const language = "java";
Expand All @@ -93,18 +97,25 @@ const resolveCloseConfig = (language, framework = '') => {
* renderOnClose();
*/
export function OnClose({ language, framework = '', title }) {
let onCloseMethod = '';
let indent = 0;

if (websocketOnCloseMethod[language]) {
const generateOnCloseCode = resolveCloseConfig(language, framework);
if (!generateOnCloseCode) {
return <Text indent={0}>{''}</Text>;
}
const closeResult = generateOnCloseCode(title);
onCloseMethod = closeResult.onCloseMethod;
indent = closeResult.indent ?? 0;
const supportedLanguages = Object.keys(websocketOnCloseMethod);

if (!websocketOnCloseMethod[language]) {
throw unsupportedLanguage(language, supportedLanguages);
}

const generateOnCloseCode = resolveCloseConfig(language, framework);

if (typeof generateOnCloseCode !== 'function') {
const supportedFrameworks = Object.keys(websocketOnCloseMethod[language] || {});
throw unsupportedFramework(language, framework, supportedFrameworks);
}

const closeResult = generateOnCloseCode(title);
const { onCloseMethod } = closeResult;

indent = closeResult.indent ?? 0;

return (
<Text indent={indent}>
Expand Down
17 changes: 11 additions & 6 deletions packages/components/src/components/OnError.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedLanguage } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart'} Language
Expand Down Expand Up @@ -54,6 +55,7 @@ const websocketOnErrorMethod = {
* @param {Object} props - Component props.
* @param {Language} props.language - The programming language for which to generate onError handler code.
* @returns {JSX.Element} A Text component containing the onError handler code for the specified language.
* @throws {Error} When the specified language is not supported.
*
* @example
* const language = "javascript";
Expand All @@ -67,14 +69,17 @@ const websocketOnErrorMethod = {
* renderOnError();
*/
export function OnError({ language }) {
let onErrorMethod = '';
const supportedLanguages = Object.keys(websocketOnErrorMethod);

if (websocketOnErrorMethod[language]) {
const generateErrorCode = websocketOnErrorMethod[language];
const errorResult = generateErrorCode();
onErrorMethod = errorResult.onErrorMethod;
}
const generateErrorCode = websocketOnErrorMethod[language];

if (!generateErrorCode) {
throw unsupportedLanguage(language, supportedLanguages);
}

const errorResult = generateErrorCode();
const { onErrorMethod } = errorResult;

return (
<Text>
{onErrorMethod}
Expand Down
15 changes: 9 additions & 6 deletions packages/components/src/components/OnMessage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Text } from '@asyncapi/generator-react-sdk';
import { unsupportedLanguage } from '../utils/ErrorHandling';

/**
* @typedef {'python' | 'javascript' | 'dart'} Language
Expand Down Expand Up @@ -82,7 +83,7 @@ const websocketOnMessageMethod = {
* @param {Object} props - Component props.
* @param {Language} props.language - The programming language for which to generate onMessage handler code.
* @returns {JSX.Element} A Text component containing the onMessage handler code for the specified language.
*
* @throws {Error} When the specified language is not supported.
* @example
* const language = "javascript";
*
Expand All @@ -95,13 +96,15 @@ const websocketOnMessageMethod = {
* renderOnMessage();
*/
export function OnMessage({ language }) {
let onMessageMethod = '';
const supportedLanguages = Object.keys(websocketOnMessageMethod);
const generateOnMessageCode = websocketOnMessageMethod[language];

if (websocketOnMessageMethod[language]) {
const generateOnMessageCode = websocketOnMessageMethod[language];
const messageResult = generateOnMessageCode();
onMessageMethod = messageResult.onMessageMethod;
if (!generateOnMessageCode) {
throw unsupportedLanguage(language, supportedLanguages);
}

const messageResult = generateOnMessageCode();
const { onMessageMethod } = messageResult;

return (
<Text>
Expand Down
Loading