Skip to content

Commit

Permalink
Pull out InvokeMethod
Browse files Browse the repository at this point in the history
  • Loading branch information
fortuna committed Dec 3, 2024
1 parent 3febe62 commit e5bc49a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 44 deletions.
25 changes: 14 additions & 11 deletions client/electron/go_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import koffi from 'koffi';

import {pathToBackendLibrary} from './app_paths';

let invokeGoAPIFunc: Function | undefined;
let invokeMethodFunc: Function | undefined;

/**
* Calls a Go function by invoking the `InvokeGoAPI` function in the native backend library.
* Calls a Go function by invoking the `InvokeMethod` function in the native backend library.
*
* @param method The name of the Go method to invoke.
* @param input The input string to pass to the API.
Expand All @@ -32,8 +32,11 @@ let invokeGoAPIFunc: Function | undefined;
* Ensure that the function signature and data structures are consistent with the C definitions
* in `./client/go/outline/electron/go_plugin.go`.
*/
export async function invokeGoApi(method: string, input: string): Promise<string> {
if (!invokeGoAPIFunc) {
export async function invokeMethod(
method: string,
input: string
): Promise<string> {
if (!invokeMethodFunc) {
const backendLib = koffi.load(pathToBackendLibrary());

// Define C strings and setup auto release
Expand All @@ -43,19 +46,19 @@ export async function invokeGoApi(method: string, input: string): Promise<string
backendLib.func('FreeCGoString', 'void', ['str'])
);

// Define InvokeGoAPI data structures and function
const invokeGoApiResult = koffi.struct('InvokeGoAPIResult', {
// Define InvokeMethod data structures and function
const invokeGoApiResult = koffi.struct('InvokeMethodResult', {
Output: cgoString,
ErrorJson: cgoString,
});
invokeGoAPIFunc = promisify(
backendLib.func('InvokeGoAPI', invokeGoApiResult, ['str', 'str']).async
invokeMethodFunc = promisify(
backendLib.func('InvokeMethod', invokeGoApiResult, ['str', 'str']).async
);
}

console.debug('[Backend] - calling InvokeGoAPI ...');
const result = await invokeGoAPIFunc(method, input);
console.debug('[Backend] - InvokeGoAPI returned', result);
console.debug('[Backend] - calling InvokeMethod ...');
const result = await invokeMethodFunc(method, input);
console.debug('[Backend] - InvokeMethod returned', result);
if (result.ErrorJson) {
throw Error(result.ErrorJson);
}
Expand Down
4 changes: 2 additions & 2 deletions client/electron/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
import {autoUpdater} from 'electron-updater';

import {lookupIp} from './connectivity';
import {invokeGoApi} from './go_plugin';
import {invokeMethod} from './go_plugin';
import {GoVpnTunnel} from './go_vpn_tunnel';
import {installRoutingServices, RoutingDaemon} from './routing_service';
import {TunnelStore} from './tunnel_store';
Expand Down Expand Up @@ -511,7 +511,7 @@ function main() {
ipcMain.handle(
'outline-ipc-invoke-method',
(_, method: string, params: string): Promise<string> => {
return invokeGoApi(method, params);
return invokeMethod(method, params);
}
);

Expand Down
41 changes: 10 additions & 31 deletions client/go/outline/electron/go_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ package main
/*
#include <stdlib.h> // for C.free
// InvokeGoAPIResult is a struct used to pass result from Go to TypeScript boundary.
typedef struct InvokeGoAPIResult_t
// InvokeMethodResult is a struct used to pass result from Go to TypeScript boundary.
typedef struct InvokeMethodResult_t
{
// A string representing the result of the Go function call.
// This may be a raw string or a JSON string depending on the API call.
Expand All @@ -28,7 +28,7 @@ typedef struct InvokeGoAPIResult_t
// Go function call, or NULL if no error occurred.
// This error can be parsed by the PlatformError in TypeScript.
const char *ErrorJson;
} InvokeGoAPIResult;
} InvokeMethodResult;
*/
import "C"
import (
Expand All @@ -41,40 +41,19 @@ import (
"github.com/Jigsaw-Code/outline-apps/client/go/outline/platerrors"
)

// API name constants
const (
// FetchResourceAPI fetches a resource located at a given URL.
//
// - Input: the URL string of the resource to fetch
// - Output: the content in raw string of the fetched resource
FetchResourceAPI = "FetchResource"
)

// InvokeGoAPI is the unified entry point for TypeScript to invoke various Go functions.
// InvokeMethod is the unified entry point for TypeScript to invoke various Go functions.
//
// The input and output are all defined as string, but they may represent either a raw string,
// or a JSON string depending on the API call.
//
// Check the API name constants comment for more details about the input and output format.
//
//export InvokeGoAPI
func InvokeGoAPI(api *C.char, input *C.char) C.InvokeGoAPIResult {
apiName := C.GoString(api)
switch apiName {

case FetchResourceAPI:
res := outline.FetchResource(C.GoString(input))
return C.InvokeGoAPIResult{
Output: newCGoString(res.Content),
ErrorJson: marshalCGoErrorJson(platerrors.ToPlatformError(res.Error)),
}

default:
err := &platerrors.PlatformError{
Code: platerrors.InternalError,
Message: fmt.Sprintf("unsupported Go API: %s", apiName),
}
return C.InvokeGoAPIResult{ErrorJson: marshalCGoErrorJson(err)}
//export InvokeMethod
func InvokeMethod(method *C.char, input *C.char) C.InvokeMethodResult {
result := outline.InvokeMethod(C.GoString(method), C.GoString(input))
return C.InvokeMethodResult{
Output: newCGoString(result.Value),
ErrorJson: marshalCGoErrorJson(result.Error),
}
}

Expand Down
57 changes: 57 additions & 0 deletions client/go/outline/method_channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package outline

import (
"fmt"

"github.com/Jigsaw-Code/outline-apps/client/go/outline/platerrors"
)

// API name constants
const (
// FetchResourceMethod fetches a resource located at a given URL.
//
// - Input: the URL string of the resource to fetch
// - Output: the content in raw string of the fetched resource
MethodFetchResource = "FetchResource"
)

// InvokeMethodResult represents the result of an InvokeMethod call.
//
// We use a struct instead of a tuple to preserve a strongly typed error that gobind recognizes.
type InvokeMethodResult struct {
Value string
Error *platerrors.PlatformError
}

// InvokeMethod calls a method by name.
func InvokeMethod(method string, input string) *InvokeMethodResult {
switch method {
case MethodFetchResource:
url := input
result := FetchResource(url)
return &InvokeMethodResult{
Value: result.Content,
Error: result.Error,
}

default:
return &InvokeMethodResult{Error: &platerrors.PlatformError{
Code: platerrors.InternalError,
Message: fmt.Sprintf("unsupported Go method: %s", method),
}}
}
}

0 comments on commit e5bc49a

Please sign in to comment.