mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
Add gRPC support (#5148)
This commit is contained in:
127
packages/bruno-app/src/utils/network/grpc-event-listeners.js
Normal file
127
packages/bruno-app/src/utils/network/grpc-event-listeners.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { useEffect } from 'react';
|
||||
import { grpcResponseReceived, runGrpcRequestEvent } from 'providers/ReduxStore/slices/collections/index';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
import { updateActiveConnectionsInStore } from 'providers/ReduxStore/slices/collections/actions';
|
||||
|
||||
const useGrpcEventListeners = () => {
|
||||
const { ipcRenderer } = window;
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isElectron()) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
ipcRenderer.invoke('renderer:ready');
|
||||
|
||||
// Handle gRPC requestSent event
|
||||
const removeGrpcRequestSentListener = ipcRenderer.on('grpc:request', (requestId, collectionUid, eventData) => {
|
||||
|
||||
dispatch(runGrpcRequestEvent({
|
||||
eventType: "request",
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
requestUid: requestId,
|
||||
eventData
|
||||
}));
|
||||
});
|
||||
|
||||
const removeGrpcMessageSentListener = ipcRenderer.on('grpc:message', (requestId, collectionUid, eventData) => {
|
||||
|
||||
dispatch(runGrpcRequestEvent({
|
||||
eventType: "message",
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
requestUid: requestId,
|
||||
eventData
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC response event (for unary calls and streaming)
|
||||
const removeGrpcResponseListener = ipcRenderer.on(`grpc:response`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'response',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC metadata
|
||||
const removeGrpcMetadataListener = ipcRenderer.on(`grpc:metadata`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'metadata',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC status updates
|
||||
const removeGrpcStatusListener = ipcRenderer.on(`grpc:status`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'status',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC errors
|
||||
const removeGrpcErrorListener = ipcRenderer.on(`grpc:error`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'error',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC end event
|
||||
const removeGrpcEndListener = ipcRenderer.on(`grpc:server-end-stream`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'end',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
// Handle gRPC cancel event
|
||||
const removeGrpcCancelListener = ipcRenderer.on(`grpc:server-cancel-stream`, (requestId, collectionUid, data) => {
|
||||
|
||||
dispatch(grpcResponseReceived({
|
||||
itemUid: requestId,
|
||||
collectionUid: collectionUid,
|
||||
eventType: 'cancel',
|
||||
eventData: data
|
||||
}));
|
||||
});
|
||||
|
||||
const removeGrpcConnectionsChangedListener = ipcRenderer.on(`grpc:connections-changed`, (data) => {
|
||||
|
||||
dispatch(updateActiveConnectionsInStore(data));
|
||||
});
|
||||
|
||||
return () => {
|
||||
removeGrpcRequestSentListener();
|
||||
removeGrpcMessageSentListener();
|
||||
removeGrpcResponseListener();
|
||||
removeGrpcMetadataListener();
|
||||
removeGrpcStatusListener();
|
||||
removeGrpcErrorListener();
|
||||
removeGrpcEndListener();
|
||||
removeGrpcCancelListener();
|
||||
removeGrpcConnectionsChangedListener();
|
||||
};
|
||||
|
||||
}, [isElectron]);
|
||||
};
|
||||
|
||||
export default useGrpcEventListeners;
|
||||
@@ -1,5 +1,3 @@
|
||||
import { safeStringifyJSON } from 'utils/common';
|
||||
|
||||
export const sendNetworkRequest = async (item, collection, environment, runtimeVariables) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (['http-request', 'graphql-request'].includes(item.type)) {
|
||||
@@ -27,6 +25,23 @@ export const sendNetworkRequest = async (item, collection, environment, runtimeV
|
||||
});
|
||||
};
|
||||
|
||||
export const sendGrpcRequest = async (item, collection, environment, runtimeVariables) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
startGrpcRequest(item, collection, environment, runtimeVariables)
|
||||
.then((initialState) => {
|
||||
// Return an initial state object to update the UI
|
||||
// The real response data will be handled by event listeners
|
||||
resolve({
|
||||
...initialState,
|
||||
timeline: []
|
||||
});
|
||||
})
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
const sendHttpRequest = async (item, collection, environment, runtimeVariables) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
@@ -60,3 +75,141 @@ export const cancelNetworkRequest = async (cancelTokenUid) => {
|
||||
ipcRenderer.invoke('cancel-http-request', cancelTokenUid).then(resolve).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const startGrpcRequest = async (item, collection, environment, runtimeVariables) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
const request = item.draft ? item.draft : item;
|
||||
|
||||
ipcRenderer.invoke('grpc:start-connection', {
|
||||
request,
|
||||
collection,
|
||||
environment,
|
||||
runtimeVariables
|
||||
})
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a message to an existing gRPC stream
|
||||
* @param {string} requestId - The request ID to send a message to
|
||||
* @param {Object} message - The message to send
|
||||
* @returns {Promise<Object>} - The result of the send operation
|
||||
*/
|
||||
export const sendGrpcMessage = async (item, collectionUid, message) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:send-message', item.uid, collectionUid, message)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels a running gRPC request
|
||||
* @param {string} requestId - The request ID to cancel
|
||||
* @returns {Promise<Object>} - The result of the cancel operation
|
||||
*/
|
||||
export const cancelGrpcRequest = async (requestId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:cancel', requestId)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Ends a gRPC streaming request (client-streaming or bidirectional)
|
||||
* @param {string} requestId - The request ID to end
|
||||
* @returns {Promise<Object>} - The result of the end operation
|
||||
*/
|
||||
export const endGrpcStream = async (requestId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:end', requestId)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const loadGrpcMethodsFromProtoFile = async (filePath, includeDirs = []) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:load-methods-proto', { filePath, includeDirs }).then(resolve).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
// export const getGrpcMethodsFromReflection = async (request, collection, environment, runtimeVariables) => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const { ipcRenderer } = window;
|
||||
// ipcRenderer.invoke('grpc:load-methods-reflection', { request, collection, environment, runtimeVariables }).then(resolve).catch(reject);
|
||||
// });
|
||||
// };
|
||||
|
||||
export const cancelGrpcConnection = async (connectionId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:cancel-request', { requestId: connectionId }).then(resolve).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
export const endGrpcConnection = async (connectionId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:end-request', { requestId: connectionId }).then(resolve).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a gRPC connection is active
|
||||
* @param {string} connectionId - The connection ID to check
|
||||
* @returns {Promise<boolean>} - Whether the connection is active
|
||||
*/
|
||||
export const isGrpcConnectionActive = async (connectionId) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
ipcRenderer.invoke('grpc:is-connection-active', connectionId)
|
||||
.then(response => {
|
||||
if (response.success) {
|
||||
resolve(response.isActive);
|
||||
} else {
|
||||
// If there was an error, assume the connection is not active
|
||||
console.error('Error checking connection status:', response.error);
|
||||
resolve(false);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to check connection status:', err);
|
||||
// On error, assume the connection is not active
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a sample gRPC message for a method
|
||||
* @param {string} methodPath - The full gRPC method path
|
||||
* @param {string|null} existingMessage - Optional existing message JSON string to use as a template
|
||||
* @param {Object} options - Additional options for message generation
|
||||
* @returns {Promise<Object>} The generated sample message or error
|
||||
*/
|
||||
export const generateGrpcSampleMessage = async (methodPath, existingMessage = null, options = {}) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
ipcRenderer.invoke('grpc:generate-sample-message', {
|
||||
methodPath,
|
||||
existingMessage,
|
||||
options
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user