From 7fe00bc427ebf9b29f4754623d9062b8391a19e6 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:35:11 +0530 Subject: [PATCH 1/9] feat(playground): add default HTTP execution plugin --- playground/components/plugins/HttpPlugin.tsx | 137 +++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 playground/components/plugins/HttpPlugin.tsx diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx new file mode 100644 index 000000000..2f0da0778 --- /dev/null +++ b/playground/components/plugins/HttpPlugin.tsx @@ -0,0 +1,137 @@ +import React, { useState } from 'react'; +import { AsyncApiPlugin, PluginAPI, PluginSlot, ComponentSlotProps } from '@asyncapi/react-component'; + +const HttpExecutionComponent: React.FC = ({ context }) => { + const { schema } = context; + const { operation, channel } = schema as any; + + const hasHttpBinding = channel?.bindings()?.has('http') || operation?.bindings()?.has('http'); + const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; + + if (!hasHttpBinding || isWebhook) { + return null; + } + + const [response, setResponse] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const handleExecute = async () => { + setLoading(true); + setError(null); + setResponse(null); + + try { + const servers = typeof channel?.servers === 'function' && channel.servers() ? channel.servers().all() : []; + const serverUrl = servers.length > 0 ? servers[0].url() : window.location.origin; + const address = channel?.address() || ''; + + const baseUrl = serverUrl.replace(/\/$/, ''); + const path = address.startsWith('/') ? address : `/${address}`; + const fullUrl = `${baseUrl}${path}`; + + const operationBinding = operation?.bindings()?.get('http'); + const channelBinding = channel?.bindings()?.get('http'); + const method = operationBinding?.method || channelBinding?.method || 'GET'; + + const res = await fetch(fullUrl, { + method: method.toUpperCase(), + headers: { + 'Accept': 'application/json, text/plain, */*', + }, + }); + + const data = await res.text(); + let parsedData: any = data; + try { + parsedData = JSON.parse(data); + } catch (e) { + // Leave as string if not JSON + } + + setResponse({ + status: res.status, + statusText: res.statusText, + headers: Object.fromEntries(res.headers.entries()), + data: parsedData, + }); + } catch (err: any) { + setError(err.message || 'Network failure. Ensure CORS is configured correctly and the server is reachable.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+

HTTP Execution

+

Fire a real HTTP request using the operation's server bindings.

+
+ +
+ + {error && ( +
+
Request Failed
+

{error}

+
+ )} + + {response && ( +
+
+ = 200 && response.status < 300 + ? 'bg-green-100 text-green-800' + : 'bg-red-100 text-red-800' + }`}> + {response.status} {response.statusText} + +
+ +
+
+
Response Body
+
+
+
+                {typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2)}
+              
+
+
+ +
+ + + + + Response Headers + +
+
+                {JSON.stringify(response.headers, null, 2)}
+              
+
+
+
+ )} +
+ ); +}; + +export const httpPlugin: AsyncApiPlugin = { + name: 'default-http-plugin', + version: '1.0.0', + description: 'Playground default plugin for HTTP execution', + install(api: PluginAPI) { + api.registerComponent(PluginSlot.OPERATION, HttpExecutionComponent); + } +}; From 917086e830115b006a8675b1fd5a2a77b8076103 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:35:41 +0530 Subject: [PATCH 2/9] feat(playground): add default Webhook simulation plugin --- .../components/plugins/WebhookPlugin.tsx | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 playground/components/plugins/WebhookPlugin.tsx diff --git a/playground/components/plugins/WebhookPlugin.tsx b/playground/components/plugins/WebhookPlugin.tsx new file mode 100644 index 000000000..9bf670460 --- /dev/null +++ b/playground/components/plugins/WebhookPlugin.tsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react'; +import { AsyncApiPlugin, PluginAPI, PluginSlot, ComponentSlotProps } from '@asyncapi/react-component'; + +const WebhookExecutionComponent: React.FC = ({ context }) => { + const { schema } = context; + const { operation } = schema as any; + + const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; + + if (!isWebhook) { + return null; + } + + const [endpoint, setEndpoint] = useState(''); + const [response, setResponse] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + // Extract expected payload from the first message + const messages = operation.messages ? operation.messages().all() : []; + const firstMessage = messages.length > 0 ? messages[0] : null; + const payloadSchema = firstMessage?.payload ? firstMessage.payload().json() : null; + + // A simple placeholder payload generation based on type + const generateMockPayload = (schema: any) => { + if (!schema) return {}; + if (schema.example) return schema.example; + if (schema.examples && schema.examples.length > 0) return schema.examples[0]; + if (schema.type === 'object' && schema.properties) { + const obj: any = {}; + Object.keys(schema.properties).forEach(k => { + obj[k] = schema.properties[k].type === 'string' ? 'string' : + schema.properties[k].type === 'integer' ? 1 : + schema.properties[k].type === 'boolean' ? true : null; + }); + return obj; + } + return { mock: "payload" }; + }; + + const expectedPayload = generateMockPayload(payloadSchema); + const payloadString = JSON.stringify(expectedPayload, null, 2); + + const handleSimulate = async () => { + if (!endpoint) { + setError("Please provide an endpoint URL to simulate delivery."); + return; + } + setLoading(true); + setError(null); + setResponse(null); + + try { + const res = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json, text/plain, */*', + }, + body: payloadString, + }); + + const data = await res.text(); + let parsedData: any = data; + try { + parsedData = JSON.parse(data); + } catch (e) { + } + + setResponse({ + status: res.status, + statusText: res.statusText, + headers: Object.fromEntries(res.headers.entries()), + data: parsedData, + }); + } catch (err: any) { + setError(err.message || 'Simulation failed. Ensure the endpoint is reachable and configured for CORS.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

+ + + + Webhook Simulation +

+

Simulate delivery of the expected incoming payload to a configured endpoint.

+
+ +
+
+ + setEndpoint(e.target.value)} + placeholder="https://your-server.com/webhook" + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm" + /> +
+ +
+ +
+            {payloadString}
+          
+
+ +
+ +
+
+ + {error && ( +
+
Simulation Failed
+

{error}

+
+ )} + + {response && ( +
+
+ = 200 && response.status < 300 + ? 'bg-green-100 text-green-800' + : 'bg-red-100 text-red-800' + }`}> + {response.status} {response.statusText} + +
+ +
+
+
Response Body
+
+
+
+                {typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2)}
+              
+
+
+
+ )} +
+ ); +}; + +export const webhookPlugin: AsyncApiPlugin = { + name: 'default-webhook-plugin', + version: '1.0.0', + description: 'Playground default plugin for Webhook simulation', + install(api: PluginAPI) { + api.registerComponent(PluginSlot.OPERATION, WebhookExecutionComponent); + } +}; From c569e1eb533622d15023a8d5555fd153d23115d6 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:35:49 +0530 Subject: [PATCH 3/9] feat(playground): export execution plugins --- playground/components/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/playground/components/index.ts b/playground/components/index.ts index 9fc998453..7643b05ee 100644 --- a/playground/components/index.ts +++ b/playground/components/index.ts @@ -5,3 +5,5 @@ export { default as SplitWrapper } from './SplitWrapper'; export { default as Tabs } from './Tabs'; export { default as Tab } from './Tab'; export * from './styled'; +export * from './plugins/HttpPlugin'; +export * from './plugins/WebhookPlugin'; From 48c7dc140c18e1b44a48a6772e63ddab05d56f20 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:36:02 +0530 Subject: [PATCH 4/9] feat(playground): register HTTP and Webhook execution plugins --- playground/app/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/playground/app/page.tsx b/playground/app/page.tsx index 66c128f42..1147afcc9 100644 --- a/playground/app/page.tsx +++ b/playground/app/page.tsx @@ -13,6 +13,8 @@ import { CodeEditorsWrapper, AsyncApiWrapper, SplitWrapper, + httpPlugin, + webhookPlugin, } from '@/components'; import { defaultConfig, parse, debounce } from '@/utils'; import * as specs from '@/specs'; @@ -93,7 +95,7 @@ class Playground extends Component { - + From abe176980270b6ad293099e0c1146cecce9d387b Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:39:09 +0530 Subject: [PATCH 5/9] fix(playground): use inline PluginContext to replace missing ComponentSlotProps export --- playground/components/plugins/HttpPlugin.tsx | 4 ++-- playground/components/plugins/WebhookPlugin.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx index 2f0da0778..3831d4089 100644 --- a/playground/components/plugins/HttpPlugin.tsx +++ b/playground/components/plugins/HttpPlugin.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; -import { AsyncApiPlugin, PluginAPI, PluginSlot, ComponentSlotProps } from '@asyncapi/react-component'; +import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; -const HttpExecutionComponent: React.FC = ({ context }) => { +const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; const { operation, channel } = schema as any; diff --git a/playground/components/plugins/WebhookPlugin.tsx b/playground/components/plugins/WebhookPlugin.tsx index 9bf670460..56121aa2d 100644 --- a/playground/components/plugins/WebhookPlugin.tsx +++ b/playground/components/plugins/WebhookPlugin.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; -import { AsyncApiPlugin, PluginAPI, PluginSlot, ComponentSlotProps } from '@asyncapi/react-component'; +import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; -const WebhookExecutionComponent: React.FC = ({ context }) => { +const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; const { operation } = schema as any; From 008e1b4c36777f73d82c815e417049ac0a173410 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 01:52:14 +0530 Subject: [PATCH 6/9] fix(playground): resolve SonarQube code quality issues in execution plugins --- playground/components/plugins/HttpPlugin.tsx | 15 +++++---- .../components/plugins/WebhookPlugin.tsx | 33 ++++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx index 3831d4089..93781c4fa 100644 --- a/playground/components/plugins/HttpPlugin.tsx +++ b/playground/components/plugins/HttpPlugin.tsx @@ -3,7 +3,12 @@ import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; - const { operation, channel } = schema as any; + const operation = (schema as any)?.operation; + const channel = (schema as any)?.channel; + + const [response, setResponse] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); const hasHttpBinding = channel?.bindings()?.has('http') || operation?.bindings()?.has('http'); const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; @@ -12,10 +17,6 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context return null; } - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - const handleExecute = async () => { setLoading(true); setError(null); @@ -23,7 +24,7 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context try { const servers = typeof channel?.servers === 'function' && channel.servers() ? channel.servers().all() : []; - const serverUrl = servers.length > 0 ? servers[0].url() : window.location.origin; + const serverUrl = servers.length > 0 ? servers[0].url() : globalThis.location.origin; const address = channel?.address() || ''; const baseUrl = serverUrl.replace(/\/$/, ''); @@ -46,7 +47,7 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context try { parsedData = JSON.parse(data); } catch (e) { - // Leave as string if not JSON + console.debug('Response is not valid JSON, keeping as string', e); } setResponse({ diff --git a/playground/components/plugins/WebhookPlugin.tsx b/playground/components/plugins/WebhookPlugin.tsx index 56121aa2d..747fe1ff6 100644 --- a/playground/components/plugins/WebhookPlugin.tsx +++ b/playground/components/plugins/WebhookPlugin.tsx @@ -3,7 +3,12 @@ import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; - const { operation } = schema as any; + const operation = (schema as any)?.operation; + + const [endpoint, setEndpoint] = useState(''); + const [response, setResponse] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; @@ -11,11 +16,6 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte return null; } - const [endpoint, setEndpoint] = useState(''); - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - // Extract expected payload from the first message const messages = operation.messages ? operation.messages().all() : []; const firstMessage = messages.length > 0 ? messages[0] : null; @@ -29,9 +29,16 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte if (schema.type === 'object' && schema.properties) { const obj: any = {}; Object.keys(schema.properties).forEach(k => { - obj[k] = schema.properties[k].type === 'string' ? 'string' : - schema.properties[k].type === 'integer' ? 1 : - schema.properties[k].type === 'boolean' ? true : null; + const propType = schema.properties[k].type; + if (propType === 'string') { + obj[k] = 'string'; + } else if (propType === 'integer') { + obj[k] = 1; + } else if (propType === 'boolean') { + obj[k] = true; + } else { + obj[k] = null; + } }); return obj; } @@ -65,6 +72,7 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte try { parsedData = JSON.parse(data); } catch (e) { + console.debug('Failed to parse webhook response as JSON', e); } setResponse({ @@ -94,8 +102,9 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte
- + setEndpoint(e.target.value)} @@ -105,8 +114,8 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte
- -
+          
+          
             {payloadString}
           
From 1b05390915c827e8d276956e58ab64e1e607c48b Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 02:10:11 +0530 Subject: [PATCH 7/9] fix(playground): resolve SonarQube duplication and assertion errors --- .../components/plugins/ExecutionResult.tsx | 62 +++++++++++++++++++ playground/components/plugins/HttpPlugin.tsx | 52 ++-------------- .../components/plugins/WebhookPlugin.tsx | 36 ++--------- 3 files changed, 71 insertions(+), 79 deletions(-) create mode 100644 playground/components/plugins/ExecutionResult.tsx diff --git a/playground/components/plugins/ExecutionResult.tsx b/playground/components/plugins/ExecutionResult.tsx new file mode 100644 index 000000000..efd7fd56c --- /dev/null +++ b/playground/components/plugins/ExecutionResult.tsx @@ -0,0 +1,62 @@ +import React from 'react'; + +interface ExecutionResultProps { + error: string | null; + response: any; + title?: string; + errorMessage?: string; +} + +export const ExecutionResult: React.FC = ({ error, response, errorMessage = 'Request Failed' }) => { + return ( + <> + {error && ( +
+
{errorMessage}
+

{error}

+
+ )} + + {response && ( +
+
+ = 200 && response.status < 300 + ? 'bg-green-100 text-green-800' + : 'bg-red-100 text-red-800' + }`}> + {response.status} {response.statusText} + +
+ +
+
+
Response Body
+
+
+
+                {typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2)}
+              
+
+
+ + {response.headers && ( +
+ + + + + Response Headers + +
+
+                  {JSON.stringify(response.headers, null, 2)}
+                
+
+
+ )} +
+ )} + + ); +}; diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx index 93781c4fa..7e3aa2d59 100644 --- a/playground/components/plugins/HttpPlugin.tsx +++ b/playground/components/plugins/HttpPlugin.tsx @@ -1,10 +1,12 @@ import React, { useState } from 'react'; import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; +import { ExecutionResult } from './ExecutionResult'; const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; - const operation = (schema as any)?.operation; - const channel = (schema as any)?.channel; + const schemaObj = schema as Record; + const operation = schemaObj?.operation; + const channel = schemaObj?.channel; const [response, setResponse] = useState(null); const [error, setError] = useState(null); @@ -79,51 +81,7 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context
- {error && ( -
-
Request Failed
-

{error}

-
- )} - - {response && ( -
-
- = 200 && response.status < 300 - ? 'bg-green-100 text-green-800' - : 'bg-red-100 text-red-800' - }`}> - {response.status} {response.statusText} - -
- -
-
-
Response Body
-
-
-
-                {typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2)}
-              
-
-
- -
- - - - - Response Headers - -
-
-                {JSON.stringify(response.headers, null, 2)}
-              
-
-
-
- )} + ); }; diff --git a/playground/components/plugins/WebhookPlugin.tsx b/playground/components/plugins/WebhookPlugin.tsx index 747fe1ff6..d50b35d88 100644 --- a/playground/components/plugins/WebhookPlugin.tsx +++ b/playground/components/plugins/WebhookPlugin.tsx @@ -1,9 +1,11 @@ import React, { useState } from 'react'; import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; +import { ExecutionResult } from './ExecutionResult'; const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; - const operation = (schema as any)?.operation; + const schemaObj = schema as Record; + const operation = schemaObj?.operation; const [endpoint, setEndpoint] = useState(''); const [response, setResponse] = useState(null); @@ -131,37 +133,7 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte - {error && ( -
-
Simulation Failed
-

{error}

-
- )} - - {response && ( -
-
- = 200 && response.status < 300 - ? 'bg-green-100 text-green-800' - : 'bg-red-100 text-red-800' - }`}> - {response.status} {response.statusText} - -
- -
-
-
Response Body
-
-
-
-                {typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2)}
-              
-
-
-
- )} + ); }; From 00ec54fa1a96c795d48daac839c64f4b5f90274d Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 02:15:40 +0530 Subject: [PATCH 8/9] refactor(playground): eliminate execution logic duplication and remove unused prop --- .../components/plugins/ExecutionResult.tsx | 41 ++++++++++++- playground/components/plugins/HttpPlugin.tsx | 58 ++++++------------- .../components/plugins/WebhookPlugin.tsx | 38 +++--------- 3 files changed, 64 insertions(+), 73 deletions(-) diff --git a/playground/components/plugins/ExecutionResult.tsx b/playground/components/plugins/ExecutionResult.tsx index efd7fd56c..9eb91d232 100644 --- a/playground/components/plugins/ExecutionResult.tsx +++ b/playground/components/plugins/ExecutionResult.tsx @@ -3,7 +3,6 @@ import React from 'react'; interface ExecutionResultProps { error: string | null; response: any; - title?: string; errorMessage?: string; } @@ -60,3 +59,43 @@ export const ExecutionResult: React.FC = ({ error, respons ); }; + +export const useExecution = () => { + const [response, setResponse] = React.useState(null); + const [error, setError] = React.useState(null); + const [loading, setLoading] = React.useState(false); + + const parseAndSetResponse = async (res: Response) => { + const data = await res.text(); + let parsedData: any = data; + try { + parsedData = JSON.parse(data); + } catch (e) { + console.debug('Failed to parse response as JSON', e); + } + + setResponse({ + status: res.status, + statusText: res.statusText, + headers: Object.fromEntries(res.headers.entries()), + data: parsedData, + }); + }; + + const executeRequest = async (fetchPromise: Promise, defaultErrorMsg: string) => { + setLoading(true); + setError(null); + setResponse(null); + + try { + const res = await fetchPromise; + await parseAndSetResponse(res); + } catch (err: any) { + setError(err.message || defaultErrorMsg); + } finally { + setLoading(false); + } + }; + + return { response, error, loading, executeRequest, setError }; +}; diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx index 7e3aa2d59..a6c9980b4 100644 --- a/playground/components/plugins/HttpPlugin.tsx +++ b/playground/components/plugins/HttpPlugin.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; -import { ExecutionResult } from './ExecutionResult'; +import { ExecutionResult, useExecution } from './ExecutionResult'; const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; @@ -8,9 +8,7 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context const operation = schemaObj?.operation; const channel = schemaObj?.channel; - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); + const { response, error, loading, executeRequest } = useExecution(); const hasHttpBinding = channel?.bindings()?.has('http') || operation?.bindings()?.has('http'); const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; @@ -20,49 +18,27 @@ const HttpExecutionComponent: React.FC<{ context: PluginContext }> = ({ context } const handleExecute = async () => { - setLoading(true); - setError(null); - setResponse(null); + const servers = typeof channel?.servers === 'function' && channel.servers() ? channel.servers().all() : []; + const serverUrl = servers.length > 0 ? servers[0].url() : globalThis.location.origin; + const address = channel?.address() || ''; + + const baseUrl = serverUrl.replace(/\/$/, ''); + const path = address.startsWith('/') ? address : `/${address}`; + const fullUrl = `${baseUrl}${path}`; - try { - const servers = typeof channel?.servers === 'function' && channel.servers() ? channel.servers().all() : []; - const serverUrl = servers.length > 0 ? servers[0].url() : globalThis.location.origin; - const address = channel?.address() || ''; - - const baseUrl = serverUrl.replace(/\/$/, ''); - const path = address.startsWith('/') ? address : `/${address}`; - const fullUrl = `${baseUrl}${path}`; + const operationBinding = operation?.bindings()?.get('http'); + const channelBinding = channel?.bindings()?.get('http'); + const method = operationBinding?.method || channelBinding?.method || 'GET'; - const operationBinding = operation?.bindings()?.get('http'); - const channelBinding = channel?.bindings()?.get('http'); - const method = operationBinding?.method || channelBinding?.method || 'GET'; - - const res = await fetch(fullUrl, { + await executeRequest( + fetch(fullUrl, { method: method.toUpperCase(), headers: { 'Accept': 'application/json, text/plain, */*', }, - }); - - const data = await res.text(); - let parsedData: any = data; - try { - parsedData = JSON.parse(data); - } catch (e) { - console.debug('Response is not valid JSON, keeping as string', e); - } - - setResponse({ - status: res.status, - statusText: res.statusText, - headers: Object.fromEntries(res.headers.entries()), - data: parsedData, - }); - } catch (err: any) { - setError(err.message || 'Network failure. Ensure CORS is configured correctly and the server is reachable.'); - } finally { - setLoading(false); - } + }), + 'Network failure. Ensure CORS is configured correctly and the server is reachable.' + ); }; return ( diff --git a/playground/components/plugins/WebhookPlugin.tsx b/playground/components/plugins/WebhookPlugin.tsx index d50b35d88..802bd5945 100644 --- a/playground/components/plugins/WebhookPlugin.tsx +++ b/playground/components/plugins/WebhookPlugin.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; -import { ExecutionResult } from './ExecutionResult'; +import { ExecutionResult, useExecution } from './ExecutionResult'; const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ context }) => { const { schema } = context; @@ -8,9 +8,7 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte const operation = schemaObj?.operation; const [endpoint, setEndpoint] = useState(''); - const [response, setResponse] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); + const { response, error, loading, executeRequest, setError } = useExecution(); const isWebhook = typeof operation?.isWebhook === 'function' ? operation.isWebhook() : false; @@ -55,39 +53,17 @@ const WebhookExecutionComponent: React.FC<{ context: PluginContext }> = ({ conte setError("Please provide an endpoint URL to simulate delivery."); return; } - setLoading(true); - setError(null); - setResponse(null); - - try { - const res = await fetch(endpoint, { + await executeRequest( + fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json, text/plain, */*', }, body: payloadString, - }); - - const data = await res.text(); - let parsedData: any = data; - try { - parsedData = JSON.parse(data); - } catch (e) { - console.debug('Failed to parse webhook response as JSON', e); - } - - setResponse({ - status: res.status, - statusText: res.statusText, - headers: Object.fromEntries(res.headers.entries()), - data: parsedData, - }); - } catch (err: any) { - setError(err.message || 'Simulation failed. Ensure the endpoint is reachable and configured for CORS.'); - } finally { - setLoading(false); - } + }), + 'Simulation failed. Ensure the endpoint is reachable and configured for CORS.' + ); }; return ( From e13d1f03d1562cc00ced120ed8885b3ad9195b29 Mon Sep 17 00:00:00 2001 From: Manik Date: Thu, 11 Jun 2026 10:35:50 +0530 Subject: [PATCH 9/9] fix(playground): remove unused useState import --- playground/components/plugins/HttpPlugin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playground/components/plugins/HttpPlugin.tsx b/playground/components/plugins/HttpPlugin.tsx index a6c9980b4..7ee1b3a19 100644 --- a/playground/components/plugins/HttpPlugin.tsx +++ b/playground/components/plugins/HttpPlugin.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { AsyncApiPlugin, PluginAPI, PluginSlot, PluginContext } from '@asyncapi/react-component'; import { ExecutionResult, useExecution } from './ExecutionResult';