feat: add variable interpolation support for WebSocket requests (#6064)

* feat: add variable interpolation support for WebSocket requests

- Add WebSocket body interpolation in interpolateVars function
- Interpolate URL, headers, and all messages in request.body.ws array with full variable context
- Refactor sendWsRequest to use main process preparation (removes duplication)
- Add mode property to wsRequest object for proper request type detection
- Ensure consistent variable precedence matching HTTP/gRPC requests
- Centralize all interpolation logic in main process via prepareWsRequest

* Add Playwright tests for WebSocket variable interpolation

- Add tests for URL interpolation (wss://echo.{{url}}.org)
- Add tests for message content interpolation ({"test": "{{data}}"})
- Update test fixtures to use wss://echo.websocket.org echo server
- Add WEBSOCKET_FLOWS.md documentation
- Refactor queueWsMessage to handle variable interpolation in main process

* removed ws flow documentation

* chore: updated the network/index.js file to reduce merge conflicts by moving around code

* fix: added collection and item to WsQueryUrl Editor to fix available variable highlight

* chore: remove unnecessary whitespace in WebSocket event handlers

---------

Co-authored-by: Sid <siddharth@usebruno.com>
This commit is contained in:
Chirag Chandrashekhar
2025-11-17 12:02:25 +05:30
committed by GitHub
parent 2d2a17c90f
commit 8ec1925b9f
9 changed files with 255 additions and 42 deletions

View File

@@ -241,25 +241,45 @@ export const connectWS = async (item, collection, environment, runtimeVariables,
});
};
export const sendWsRequest = (item, collection, environment, runtimeVariables) => {
return new Promise(async (resolve, reject) => {
const ensureConnection = async () => {
const connectionStatus = await isWsConnectionActive(item.uid);
if (!connectionStatus.isActive) {
await connectWS(item, collection, environment, runtimeVariables, { connectOnly: true });
}
};
const { request } = item.draft ? item.draft : item;
queueWsMessage(item, collection.uid, request.body.ws[0].content)
.then((initialState) => {
// Return an initial state object to update the UI
// The real response data will be handled by event listeners
resolve({
...initialState
});
})
.catch((err) => reject(err));
await ensureConnection();
export const sendWsRequest = async (item, collection, environment, runtimeVariables) => {
const ensureConnection = async () => {
const connectionStatus = await isWsConnectionActive(item.uid);
if (!connectionStatus.isActive) {
await connectWS(item, collection, environment, runtimeVariables, { connectOnly: true });
}
};
await ensureConnection();
// Use queueWsMessage helper to queue all messages with proper variable interpolation
const result = await queueWsMessage(item, collection, environment, runtimeVariables, null);
if (result.success) {
return {};
} else {
throw new Error(result.error || 'Failed to queue messages');
}
};
/**
* Queues a message to an existing WebSocket connection with variable interpolation
* @param {Object} item - The request item
* @param {Object} collection - The collection object
* @param {Object} environment - The environment variables
* @param {Object} runtimeVariables - The runtime variables
* @param {string} messageContent - The message content to queue (or null to queue all messages)
* @returns {Promise<Object>} - The result of the queue operation
*/
export const queueWsMessage = async (item, collection, environment, runtimeVariables, messageContent) => {
return new Promise((resolve, reject) => {
const { ipcRenderer } = window;
ipcRenderer.invoke('renderer:ws:queue-message', {
item,
collection,
environment,
runtimeVariables,
messageContent
}).then(resolve).catch(reject);
});
};
@@ -287,20 +307,6 @@ export const startWsConnection = async (item, collection, environment, runtimeVa
});
};
/**
* Sends a message to an existing WebSocket connection
* @param {string} requestId - The request ID to send a message to
* @param {string} collectionUid - The collection ID the message is for
* @param {*} message - The message
* @returns {Promise<Object>} - The result of the send operation
*/
export const queueWsMessage = async (item, collectionUid, message) => {
return new Promise((resolve, reject) => {
const { ipcRenderer } = window;
ipcRenderer.invoke('renderer:ws:queue-message', item.uid, collectionUid, message).then(resolve).catch(reject);
});
};
/**
* Sends a message to an existing WebSocket connection
* @param {string} requestId - The request ID to send a message to