Add gRPC support (#5148)

This commit is contained in:
sanish chirayath
2025-08-20 16:24:49 +05:30
committed by GitHub
parent e5a608f962
commit efb2e83ad9
102 changed files with 7756 additions and 841 deletions

View 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;

View File

@@ -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);
});
};