Skip to content

Commit

Permalink
Merge pull request #9 from nateshmbhat/feature-update-refactors
Browse files Browse the repository at this point in the history
Error handling feature updates
  • Loading branch information
nateshmbhat authored Jun 7, 2021
2 parents 4c0871c + 85c253b commit d670dbf
Show file tree
Hide file tree
Showing 17 changed files with 292 additions and 191 deletions.
10 changes: 8 additions & 2 deletions app/commons/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { OpenDialogReturnValue } from "electron/main";
import { loadProtos, RpcProtoInfo } from "../../renderer/behaviour";
import { appConfigStore, protoFilesStore, servicesStore } from "../../stores";
import faker from 'faker';
import { TabConfigModel, tabListConfigStore } from "../../stores/tabStore";
import { tabListConfigStore } from "../../stores/tabStore";
import { get } from "svelte/store";
import type { IncomingRequest, TabConfigModel } from "../../renderer/components/types/types";

export class ProtoUtil {
static async getMethodRpc(serviceName: string, methodName: string): Promise<RpcProtoInfo> {
Expand All @@ -23,9 +24,14 @@ export class ProtoUtil {
}
})
}

static async getMethodRpcFromRequest(incomingRequest: IncomingRequest): Promise<RpcProtoInfo> {
return this.getMethodRpc(incomingRequest.serviceName, incomingRequest.methodName)
}

static async loadProtoFilesAndStartServer(filePaths: string[], importPaths: string[]) {
const protoFiles = get(protoFilesStore).map(pf => pf.proto.filePath)
filePaths = filePaths.filter((fp) => protoFiles.indexOf(fp)<0)
filePaths = filePaths.filter((fp) => protoFiles.indexOf(fp) < 0)
if (filePaths.length == 0) return
const uniqueProtoFilePaths = [...new Set([...filePaths, ...protoFiles])];
const loadedProtoFiles = await loadProtos(uniqueProtoFilePaths, importPaths);
Expand Down
11 changes: 9 additions & 2 deletions app/main_process/grpcServer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as grpc from '@grpc/grpc-js'
import { AppConfigModel, appConfigStore } from '../stores';
import type { ProtoService } from '../renderer/behaviour';
import type { ProtoService, ResponseError } from '../renderer/behaviour';
import { responseInterceptor } from '../renderer/behaviour';
import { ipcRenderer } from 'electron';
import { RendererProcessInterface } from './ipc/ipcRendererProcessInterface';
Expand All @@ -20,7 +20,14 @@ function addGrpcServices(server: grpc.Server | null, serviceProtos: ProtoService
serviceProto.serviceName, methodRpcInfo.methodName).then((response) => {
console.log('reponse from renderer process : ')
console.dir(response, { depth: null })
callback(null, response.message)
if (response.error === undefined) {
callback(null, response.data)
}
else {
const error = response.error
console.table(error)
callback(error)
}
})
};
})
Expand Down
11 changes: 4 additions & 7 deletions app/main_process/ipc/ipcRendererProcessInterface.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { IpcChannel } from "../../commons/ipc/ipcChannelInterface"
import type { ResponseInfo } from "../../renderer/behaviour";
import { IpcRendererService } from "./ipcRendererService"

interface TransformedResponse {
message: Object
}

export class RendererProcessInterface {
static async onRequest(requestObject: any, metadata: any, serviceName: string, methodName: string): Promise<TransformedResponse> {
const ipcResponse = await IpcRendererService.send<{ data: Object }>(IpcChannel.onRequest, {
static async onRequest(requestObject: any, metadata: any, serviceName: string, methodName: string): Promise<ResponseInfo> {
const ipcResponse = await IpcRendererService.send<ResponseInfo>(IpcChannel.onRequest, {
params: {
requestObject,
metadata,
serviceName,
methodName
}
})
return { message: ipcResponse.data };
return ipcResponse
}
}
6 changes: 4 additions & 2 deletions app/renderer/behaviour/grpcClientManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ class GrpcClientManager {
});

grpcRequest.on(GRPCEventType.ERROR, (e: Error, metaInfo: ResponseMetaInformation) => {
console.error('GRPC ERROR EVENT : ', e, metaInfo);
console.warn('GRPC ERROR EVENT : ', e, metaInfo);
if (onError) onError(e, metaInfo)
});

grpcRequest.on(GRPCEventType.DATA, (data: object, metaInfo: ResponseMetaInformation) => {
if (metaInfo.stream) {
//TODO : handle streaming call
} else {
console.info('GRPC Response DATA Event : ')
console.info({ data, metaInfo }, { depth: null })
if (onResponse) onResponse(data, metaInfo)
}
});

grpcRequest.on(GRPCEventType.END, () => {
console.warn('GRPC End Event');
console.info('GRPC End Event');
if (onCallEnd) onCallEnd()
});

Expand Down
7 changes: 7 additions & 0 deletions app/renderer/behaviour/models/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ export interface ProtoService {
methods: { [key: string]: RpcProtoInfo }
}

export interface ResponseError {
code: number;
details: string;
message: string;
}

export interface ResponseInfo {
isStreaming: boolean;
data: Object;
metaInfo: ResponseMetaInformation;
error?: ResponseError
}
30 changes: 16 additions & 14 deletions app/renderer/behaviour/requestInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,38 @@ import type { Metadata } from '@grpc/grpc-js'
import { get } from 'svelte/store';
import { ProtoUtil } from '../../commons/utils';
import { activeTabConfigStore } from '../../stores';
import { EditorDataFlowMode } from '../../stores/tabStore';
import { EditorDataFlowMode, } from '../components/types/types';
import { GrpcClientManager } from './grpcClientManager';
import type { RpcProtoInfo, ResponseInfo } from './models';
import { EditorEventType } from './responseStateController';

interface RequestInterceptorCallback {
interface RequestInterceptorInput {
metadata: Metadata,
requestMessage: Object,
rpcProtoInfo: RpcProtoInfo,
}


export function requestInterceptor({ metadata, requestMessage, rpcProtoInfo }: RequestInterceptorCallback): Promise<ResponseInfo> {
export function requestInterceptor(incomingRequest: RequestInterceptorInput): Promise<ResponseInfo> {
const responsePromise = new Promise<ResponseInfo>(async (resolve, reject) => {
const config = get(activeTabConfigStore)
console.log('metadata : ', metadata)
console.log('request message : ', requestMessage)
console.log('rpc protoInfo : ', rpcProtoInfo)

console.log('Incoming request : ', incomingRequest)

activeTabConfigStore.setMonitorRequestEditorState({
...config.monitorRequestEditorState,
metadata: ProtoUtil.stringify(metadata.getMap()),
text: ProtoUtil.stringify(requestMessage)
incomingRequest: {
...config.monitorRequestEditorState.incomingRequest!,
text: ProtoUtil.stringify(incomingRequest.requestMessage),
metadata: ProtoUtil.stringify(incomingRequest.metadata.getMap()),
}
})
const transformedRequest = await requestTransformer({ metadata, requestMessage })

const transformedRequest = await requestTransformer(incomingRequest)
console.log('transformedRequest : ', transformedRequest)
GrpcClientManager.sendRequest({
metadata: ProtoUtil.stringify(transformedRequest.metadata.getMap()),
requestMessage: ProtoUtil.stringify(transformedRequest.requestMessage),
rpcProtoInfo,
rpcProtoInfo: incomingRequest.rpcProtoInfo,
url: config.targetGrpcServerUrl,
onError: (e, metaInfo) => { reject(e) },
onResponse: (data, metaInfo) => resolve({ data, isStreaming: false, metaInfo })
Expand All @@ -40,18 +43,17 @@ export function requestInterceptor({ metadata, requestMessage, rpcProtoInfo }: R
}


interface RequestTransformerInput { requestMessage: Object, metadata: Metadata }
interface RequestTransformerOutput { requestMessage: Object, metadata: Metadata }

async function requestTransformer(request: RequestTransformerInput): Promise<RequestTransformerOutput> {
async function requestTransformer(request: RequestInterceptorInput): Promise<RequestTransformerOutput> {
const transformedRequest = await new Promise<RequestTransformerOutput>(async (resolve, reject) => {
const activeTab = get(activeTabConfigStore)
if (activeTab.monitorRequestEditorState.dataFlowMode == EditorDataFlowMode.passThrough) {
resolve(request)
}
else if (activeTab.monitorRequestEditorState.dataFlowMode == EditorDataFlowMode.liveEdit) {
activeTab.monitorRequestEditorState.eventEmitter.on(EditorEventType.editingDone, async () => {
const newRequestString = get(activeTabConfigStore).monitorRequestEditorState.text;
const newRequestString = get(activeTabConfigStore).monitorRequestEditorState.incomingRequest?.text ?? '{}';
const newRequestObject = JSON.parse(newRequestString)
resolve({ metadata: request.metadata, requestMessage: newRequestObject });
})
Expand Down
18 changes: 9 additions & 9 deletions app/renderer/behaviour/responseInterceptor.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@

import type { Metadata } from '@grpc/grpc-js'
import type { Http2CallStream } from '@grpc/grpc-js/build/src/call-stream';
import { get } from 'svelte/store';
import { ProtoUtil } from '../../commons/utils';
import { activeTabConfigStore, appConfigStore, RpcOperationMode } from '../../stores';
import { EditorDataFlowMode } from '../../stores/tabStore';
import { EditorDataFlowMode } from '../components/types/types';
import type { ResponseInfo, RpcProtoInfo } from './models';
import { EditorEventType } from './responseStateController';

interface ResponseInterceptorCallback {
responseMessage: ResponseInfo
}

export async function responseInterceptor({ responseMessage }: ResponseInterceptorCallback): Promise<ResponseInfo> {
export async function responseInterceptor(responseMessage: ResponseInfo): Promise<ResponseInfo> {
const activeTabConfig = get(activeTabConfigStore)
activeTabConfigStore.setMonitorResponseEditorState({ ...activeTabConfig.monitorResponseEditorState, text: ProtoUtil.stringify(responseMessage.data) })

activeTabConfigStore.setMonitorResponseEditorState({
...activeTabConfig.monitorResponseEditorState,
incomingResponseText: ProtoUtil.stringify(responseMessage.data)
})

const transformedResponse = await transformResponse({ response: responseMessage })
return transformedResponse
}
Expand All @@ -29,7 +29,7 @@ async function transformResponse(transformerInput: ResponseTransformerInput): Pr
}
else if (activeTab.monitorResponseEditorState.dataFlowMode == EditorDataFlowMode.liveEdit) {
activeTab.monitorResponseEditorState.eventEmitter.on(EditorEventType.editingDone, async () => {
const newResponseString = get(activeTabConfigStore).monitorResponseEditorState.text
const newResponseString = get(activeTabConfigStore).monitorResponseEditorState.incomingResponseText ?? '{}'
const newResponseObject = JSON.parse(newResponseString)
resolve({ ...transformerInput.response, data: newResponseObject });
})
Expand Down
39 changes: 23 additions & 16 deletions app/renderer/components/TapRpc.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
} from 'svelte-materialify/src'
import {
activeTabConfigStore,
TabConfigModel,
tabListConfigStore,
} from '../../stores/tabStore'
import { tick } from 'svelte'
import TabCloseButton from '../pages/tabPage/components/TabCloseButton.svelte'
import type { TabConfigModel } from './types/types';
$: tabs = $tabListConfigStore.tabs
Expand All @@ -42,28 +42,28 @@
$: activeTabIndex = $tabListConfigStore.activeTabIndex
</script>

<style>
.new-tab-button {
transform: translate(10px, 10px);
}
</style>

<Tabs
centerActive
slider={false}
class="primary-text"
value={activeTabIndex}
on:change={onTabChange}>
on:change={onTabChange}
>
<div slot="tabs">
{#each tabs as tab, i (i)}
<Tab value={i}>
<div style="position:relative;text-transform: capitalize;{activeTabIndex===i ? 'font-weight:bold;' : '' }">
{#if tab.selectedRpc}{tab.selectedRpc.methodName}{:else}{'RPC'}{/if}
<span>
<TabCloseButton on:click={(e) => closeTab(i, tab)} />
</span>
</div>
</Tab>
<Tab value={i}>
<div
style="position:relative;text-transform: capitalize;{activeTabIndex ===
i
? 'font-weight:bold;'
: ''}"
>
{#if tab.selectedRpc}{tab.selectedRpc.methodName}{:else}{"RPC"}{/if}
<span>
<TabCloseButton on:click={e => closeTab(i, tab)} />
</span>
</div>
</Tab>
{/each}
<span class="new-tab-button">
<Button fab size="x-small" on:click={onNewTabClick}>
Expand All @@ -74,3 +74,10 @@

<TapRpcTab />
</Tabs>

<style>
.new-tab-button {
transform: translate(10px, 10px);
}
</style>
52 changes: 51 additions & 1 deletion app/renderer/components/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,59 @@
import type { RpcProtoInfo } from "../../behaviour";
import type { RpcOperationMode } from "../../../stores";
import type { Certificate, RpcProtoInfo } from "../../behaviour";
import type { EditorEventEmitter } from "../../behaviour/responseStateController";

export interface TabConfigModel {
id: string;
selectedRpc: RpcProtoInfo | undefined;
targetGrpcServerUrl: string;
rpcOperationMode: RpcOperationMode;
monitorRequestEditorState: MonitorRequestEditorModel;
monitorResponseEditorState: MonitorResponseEditorModel;
tlsCertificate?: Certificate,
clientRequestEditorState: ClientEditorModel;
clientResponseEditorState: ClientEditorModel;
mockRpcEditorText: string;
}

export interface ClientEditorModel {
text: string;
metadata: string;
}
export interface TabListConfigModel {
tabs: TabConfigModel[];
activeTabIndex: number;
}

///Enum only applicable for editor when it "not in client mode"
export enum EditorDataFlowMode {
passThrough, liveEdit
}


export interface MonitorRequestEditorModel {
incomingRequest?: IncomingRequest
eventEmitter: EditorEventEmitter;
dataFlowMode: EditorDataFlowMode;
}

export interface MonitorResponseEditorModel {
incomingResponseText?: string;
eventEmitter: EditorEventEmitter;
dataFlowMode: EditorDataFlowMode;
}

export interface RpcSelectorFileType {
type: 'folder' | 'file';
name: string;
//is defined only when type is 'file'
protoInfo?: RpcProtoInfo;
files: RpcSelectorFileType[]
}

export interface IncomingRequest {
serviceName: string,
methodName: string,
requestObject: any
text: string;
metadata: string;
}
Loading

0 comments on commit d670dbf

Please sign in to comment.