mirror of
https://github.com/wavetermdev/waveterm
synced 2026-04-21 14:37:16 +00:00
Add native 2+ arg RPC support and wire a concrete TestMultiArgCommand through server, generated clients, and CLI (#2963)
This PR extends WSH RPC command signatures to support `ctx + 2+ typed
args` while preserving existing `ctx` and `ctx + 1 arg` behavior. It
also adds a concrete `TestMultiArgCommand` end-to-end so the generated
Go/TS client surfaces can be inspected and exercised from CLI.
- **RPC wire + dispatch model**
- Added `wshrpc.MultiArg` (`args []any`) as the over-the-wire envelope
for 2+ arg commands.
- Extended RPC metadata to track all command arg types
(`CommandDataTypes`) and exposed a helper for normalized access.
- Updated server adapter unmarshalling to:
- decode `MultiArg` for 2+ arg commands,
- validate arg count,
- re-unmarshal each arg into its declared type before invoking typed
handlers.
- Kept single-arg commands on the existing non-`MultiArg` path.
- **Code generation (Go + TS)**
- Go codegen now emits multi-parameter wrappers for 2+ arg methods and
packs payload as `wshrpc.MultiArg`.
- TS codegen now emits multi-parameter API methods and packs payload as
`{ args: [...] }`.
- 0/1-arg generation remains unchanged to avoid wire/API churn.
- **Concrete command added for validation**
- Added to `WshRpcInterface`:
- `TestMultiArgCommand(ctx context.Context, arg1 string, arg2 int, arg3
bool) (string, error)`
- Implemented in `wshserver` with deterministic formatted return output
including source + all args.
- Updated `wsh test` command to call `TestMultiArgCommand` and print the
returned string.
- **Focused coverage**
- Added/updated targeted tests around RPC metadata and Go/TS multi-arg
codegen behavior, including command declaration for `testmultiarg`.
Example generated call shape:
```go
func TestMultiArgCommand(w *wshutil.WshRpc, arg1 string, arg2 int, arg3 bool, opts *wshrpc.RpcOpts) (string, error) {
return sendRpcRequestCallHelper[string](
w,
"testmultiarg",
wshrpc.MultiArg{Args: []any{arg1, arg2, arg3}},
opts,
)
}
```
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
This commit is contained in:
parent
9d89f4372c
commit
df24959e23
12 changed files with 263 additions and 52 deletions
|
|
@ -5,6 +5,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
|
||||
)
|
||||
|
||||
var testCmd = &cobra.Command{
|
||||
|
|
@ -20,5 +21,10 @@ func init() {
|
|||
}
|
||||
|
||||
func runTestCmd(cmd *cobra.Command, args []string) error {
|
||||
rtn, err := wshclient.TestMultiArgCommand(RpcClient, "testarg", 42, true, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
WriteStdout("%s\n", rtn)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -757,6 +757,11 @@ class RpcApiType {
|
|||
return client.wshRpcCall("test", data, opts);
|
||||
}
|
||||
|
||||
// command "testmultiarg" [call]
|
||||
TestMultiArgCommand(client: WshClient, arg1: string, arg2: number, arg3: boolean, opts?: RpcOpts): Promise<string> {
|
||||
return client.wshRpcCall("testmultiarg", { args: [arg1, arg2, arg3] }, opts);
|
||||
}
|
||||
|
||||
// command "vdomasyncinitiation" [call]
|
||||
VDomAsyncInitiationCommand(client: WshClient, data: VDomAsyncInitiationRequest, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("vdomasyncinitiation", data, opts);
|
||||
|
|
|
|||
|
|
@ -75,12 +75,7 @@ func GenerateMetaMapConsts(buf *strings.Builder, constPrefix string, rtype refle
|
|||
|
||||
func GenMethod_Call(buf *strings.Builder, methodDecl *wshrpc.WshRpcMethodDecl) {
|
||||
fmt.Fprintf(buf, "// command %q, wshserver.%s\n", methodDecl.Command, methodDecl.MethodName)
|
||||
var dataType string
|
||||
dataVarName := "nil"
|
||||
if methodDecl.CommandDataType != nil {
|
||||
dataType = ", data " + methodDecl.CommandDataType.String()
|
||||
dataVarName = "data"
|
||||
}
|
||||
dataType, dataVarName := getWshMethodDataParamsAndExpr(methodDecl)
|
||||
returnType := "error"
|
||||
respName := "_"
|
||||
tParamVal := "any"
|
||||
|
|
@ -101,12 +96,7 @@ func GenMethod_Call(buf *strings.Builder, methodDecl *wshrpc.WshRpcMethodDecl) {
|
|||
|
||||
func GenMethod_ResponseStream(buf *strings.Builder, methodDecl *wshrpc.WshRpcMethodDecl) {
|
||||
fmt.Fprintf(buf, "// command %q, wshserver.%s\n", methodDecl.Command, methodDecl.MethodName)
|
||||
var dataType string
|
||||
dataVarName := "nil"
|
||||
if methodDecl.CommandDataType != nil {
|
||||
dataType = ", data " + methodDecl.CommandDataType.String()
|
||||
dataVarName = "data"
|
||||
}
|
||||
dataType, dataVarName := getWshMethodDataParamsAndExpr(methodDecl)
|
||||
respType := "any"
|
||||
if methodDecl.DefaultResponseDataType != nil {
|
||||
respType = methodDecl.DefaultResponseDataType.String()
|
||||
|
|
@ -115,3 +105,27 @@ func GenMethod_ResponseStream(buf *strings.Builder, methodDecl *wshrpc.WshRpcMet
|
|||
fmt.Fprintf(buf, "\treturn sendRpcRequestResponseStreamHelper[%s](w, %q, %s, opts)\n", respType, methodDecl.Command, dataVarName)
|
||||
fmt.Fprintf(buf, "}\n\n")
|
||||
}
|
||||
|
||||
func getWshMethodDataParamsAndExpr(methodDecl *wshrpc.WshRpcMethodDecl) (string, string) {
|
||||
dataTypes := methodDecl.GetCommandDataTypes()
|
||||
if len(dataTypes) == 0 {
|
||||
return "", "nil"
|
||||
}
|
||||
if len(dataTypes) == 1 {
|
||||
return ", data " + dataTypes[0].String(), "data"
|
||||
}
|
||||
var paramBuilder strings.Builder
|
||||
var argBuilder strings.Builder
|
||||
for idx, dataType := range dataTypes {
|
||||
argName := fmt.Sprintf("arg%d", idx+1)
|
||||
paramBuilder.WriteString(", ")
|
||||
paramBuilder.WriteString(argName)
|
||||
paramBuilder.WriteString(" ")
|
||||
paramBuilder.WriteString(dataType.String())
|
||||
if idx > 0 {
|
||||
argBuilder.WriteString(", ")
|
||||
}
|
||||
argBuilder.WriteString(argName)
|
||||
}
|
||||
return paramBuilder.String(), fmt.Sprintf("wshrpc.MultiArg{Args: []any{%s}}", argBuilder.String())
|
||||
}
|
||||
|
|
|
|||
46
pkg/gogen/gogen_test.go
Normal file
46
pkg/gogen/gogen_test.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2026, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package gogen
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
)
|
||||
|
||||
func TestGetWshMethodDataParamsAndExpr_MultiArg(t *testing.T) {
|
||||
methodDecl := &wshrpc.WshRpcMethodDecl{
|
||||
CommandDataTypes: []reflect.Type{
|
||||
reflect.TypeOf(""),
|
||||
reflect.TypeOf(0),
|
||||
},
|
||||
}
|
||||
params, expr := getWshMethodDataParamsAndExpr(methodDecl)
|
||||
if params != ", arg1 string, arg2 int" {
|
||||
t.Fatalf("unexpected params: %q", params)
|
||||
}
|
||||
if expr != "wshrpc.MultiArg{Args: []any{arg1, arg2}}" {
|
||||
t.Fatalf("unexpected expr: %q", expr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenMethodCall_MultiArg(t *testing.T) {
|
||||
methodDecl := &wshrpc.WshRpcMethodDecl{
|
||||
Command: "test",
|
||||
CommandType: wshrpc.RpcType_Call,
|
||||
MethodName: "TestCommand",
|
||||
CommandDataTypes: []reflect.Type{reflect.TypeOf(""), reflect.TypeOf(0)},
|
||||
}
|
||||
var sb strings.Builder
|
||||
GenMethod_Call(&sb, methodDecl)
|
||||
out := sb.String()
|
||||
if !strings.Contains(out, "func TestCommand(w *wshutil.WshRpc, arg1 string, arg2 int, opts *wshrpc.RpcOpts) error {") {
|
||||
t.Fatalf("generated method missing multi-arg signature:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "sendRpcRequestCallHelper[any](w, \"test\", wshrpc.MultiArg{Args: []any{arg1, arg2}}, opts)") {
|
||||
t.Fatalf("generated method missing MultiArg payload:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
|
@ -464,16 +464,12 @@ func generateWshClientApiMethod_ResponseStream(methodDecl *wshrpc.WshRpcMethodDe
|
|||
if methodDecl.DefaultResponseDataType != nil {
|
||||
respType, _ = TypeToTSType(methodDecl.DefaultResponseDataType, tsTypesMap)
|
||||
}
|
||||
dataName := "null"
|
||||
if methodDecl.CommandDataType != nil {
|
||||
dataName = "data"
|
||||
}
|
||||
methodSigDataParams, dataName := getTsWshMethodDataParamsAndExpr(methodDecl, tsTypesMap)
|
||||
genRespType := fmt.Sprintf("AsyncGenerator<%s, void, boolean>", respType)
|
||||
if methodDecl.CommandDataType != nil {
|
||||
cmdDataTsName, _ := TypeToTSType(methodDecl.CommandDataType, tsTypesMap)
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, data: %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, cmdDataTsName, genRespType))
|
||||
} else {
|
||||
if methodSigDataParams == "" {
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, opts?: RpcOpts): %s {\n", methodDecl.MethodName, genRespType))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, methodSigDataParams, genRespType))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" return client.wshRpcStream(%q, %s, opts);\n", methodDecl.Command, dataName))
|
||||
sb.WriteString(" }\n")
|
||||
|
|
@ -488,15 +484,11 @@ func generateWshClientApiMethod_Call(methodDecl *wshrpc.WshRpcMethodDecl, tsType
|
|||
rtnTypeName, _ := TypeToTSType(methodDecl.DefaultResponseDataType, tsTypesMap)
|
||||
rtnType = fmt.Sprintf("Promise<%s>", rtnTypeName)
|
||||
}
|
||||
dataName := "null"
|
||||
if methodDecl.CommandDataType != nil {
|
||||
dataName = "data"
|
||||
}
|
||||
if methodDecl.CommandDataType != nil {
|
||||
cmdDataTsName, _ := TypeToTSType(methodDecl.CommandDataType, tsTypesMap)
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, data: %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, cmdDataTsName, rtnType))
|
||||
} else {
|
||||
methodSigDataParams, dataName := getTsWshMethodDataParamsAndExpr(methodDecl, tsTypesMap)
|
||||
if methodSigDataParams == "" {
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, opts?: RpcOpts): %s {\n", methodDecl.MethodName, rtnType))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf(" %s(client: WshClient, %s, opts?: RpcOpts): %s {\n", methodDecl.MethodName, methodSigDataParams, rtnType))
|
||||
}
|
||||
methodBody := fmt.Sprintf(" return client.wshRpcCall(%q, %s, opts);\n", methodDecl.Command, dataName)
|
||||
sb.WriteString(methodBody)
|
||||
|
|
@ -504,6 +496,30 @@ func generateWshClientApiMethod_Call(methodDecl *wshrpc.WshRpcMethodDecl, tsType
|
|||
return sb.String()
|
||||
}
|
||||
|
||||
func getTsWshMethodDataParamsAndExpr(methodDecl *wshrpc.WshRpcMethodDecl, tsTypesMap map[reflect.Type]string) (string, string) {
|
||||
dataTypes := methodDecl.GetCommandDataTypes()
|
||||
if len(dataTypes) == 0 {
|
||||
return "", "null"
|
||||
}
|
||||
if len(dataTypes) == 1 {
|
||||
cmdDataTsName, _ := TypeToTSType(dataTypes[0], tsTypesMap)
|
||||
return fmt.Sprintf("data: %s", cmdDataTsName), "data"
|
||||
}
|
||||
var methodParamBuilder strings.Builder
|
||||
var argBuilder strings.Builder
|
||||
for idx, dataType := range dataTypes {
|
||||
if idx > 0 {
|
||||
methodParamBuilder.WriteString(", ")
|
||||
argBuilder.WriteString(", ")
|
||||
}
|
||||
argName := fmt.Sprintf("arg%d", idx+1)
|
||||
cmdDataTsName, _ := TypeToTSType(dataType, tsTypesMap)
|
||||
methodParamBuilder.WriteString(fmt.Sprintf("%s: %s", argName, cmdDataTsName))
|
||||
argBuilder.WriteString(argName)
|
||||
}
|
||||
return methodParamBuilder.String(), fmt.Sprintf("{ args: [%s] }", argBuilder.String())
|
||||
}
|
||||
|
||||
func GenerateWaveObjTypes(tsTypesMap map[reflect.Type]string) {
|
||||
for _, typeUnion := range TypeUnions {
|
||||
GenerateTSTypeUnion(typeUnion, tsTypesMap)
|
||||
|
|
|
|||
28
pkg/tsgen/tsgen_wshclientapi_test.go
Normal file
28
pkg/tsgen/tsgen_wshclientapi_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2026, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tsgen
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
)
|
||||
|
||||
func TestGenerateWshClientApiMethodCall_MultiArg(t *testing.T) {
|
||||
methodDecl := &wshrpc.WshRpcMethodDecl{
|
||||
Command: "test",
|
||||
CommandType: wshrpc.RpcType_Call,
|
||||
MethodName: "TestCommand",
|
||||
CommandDataTypes: []reflect.Type{reflect.TypeOf(""), reflect.TypeOf(0)},
|
||||
}
|
||||
out := GenerateWshClientApiMethod(methodDecl, map[reflect.Type]string{})
|
||||
if !strings.Contains(out, "TestCommand(client: WshClient, arg1: string, arg2: number, opts?: RpcOpts): Promise<void> {") {
|
||||
t.Fatalf("generated method missing multi-arg signature:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "return client.wshRpcCall(\"test\", { args: [arg1, arg2] }, opts);") {
|
||||
t.Fatalf("generated method missing MultiArg payload:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
|
@ -908,6 +908,12 @@ func TestCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// command "testmultiarg", wshserver.TestMultiArgCommand
|
||||
func TestMultiArgCommand(w *wshutil.WshRpc, arg1 string, arg2 int, arg3 bool, opts *wshrpc.RpcOpts) (string, error) {
|
||||
resp, err := sendRpcRequestCallHelper[string](w, "testmultiarg", wshrpc.MultiArg{Args: []any{arg1, arg2, arg3}}, opts)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// command "vdomasyncinitiation", wshserver.VDomAsyncInitiationCommand
|
||||
func VDomAsyncInitiationCommand(w *wshutil.WshRpc, data vdom.VDomAsyncInitiationRequest, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "vdomasyncinitiation", data, opts)
|
||||
|
|
|
|||
|
|
@ -15,10 +15,14 @@ type WshRpcMethodDecl struct {
|
|||
Command string
|
||||
CommandType string
|
||||
MethodName string
|
||||
CommandDataType reflect.Type
|
||||
CommandDataTypes []reflect.Type
|
||||
DefaultResponseDataType reflect.Type
|
||||
}
|
||||
|
||||
func (decl *WshRpcMethodDecl) GetCommandDataTypes() []reflect.Type {
|
||||
return decl.CommandDataTypes
|
||||
}
|
||||
|
||||
var contextRType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
var wshRpcInterfaceRType = reflect.TypeOf((*WshRpcInterface)(nil)).Elem()
|
||||
|
||||
|
|
@ -75,11 +79,11 @@ func generateWshCommandDecl(method reflect.Method) *WshRpcMethodDecl {
|
|||
decl.Command = strings.ToLower(cmdStr)
|
||||
decl.CommandType = getWshCommandType(method)
|
||||
decl.MethodName = method.Name
|
||||
var cdataType reflect.Type
|
||||
if method.Type.NumIn() > 1 {
|
||||
cdataType = method.Type.In(1)
|
||||
var cdataTypes []reflect.Type
|
||||
for idx := 1; idx < method.Type.NumIn(); idx++ {
|
||||
cdataTypes = append(cdataTypes, method.Type.In(idx))
|
||||
}
|
||||
decl.CommandDataType = cdataType
|
||||
decl.CommandDataTypes = cdataTypes
|
||||
decl.DefaultResponseDataType = getWshMethodResponseType(decl.CommandType, method)
|
||||
return decl
|
||||
}
|
||||
|
|
|
|||
50
pkg/wshrpc/wshrpcmeta_test.go
Normal file
50
pkg/wshrpc/wshrpcmeta_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2026, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package wshrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testRpcInterfaceForDecls interface {
|
||||
NoArgCommand(ctx context.Context) error
|
||||
OneArgCommand(ctx context.Context, data string) error
|
||||
TwoArgCommand(ctx context.Context, arg1 string, arg2 int) error
|
||||
}
|
||||
|
||||
func TestGenerateWshCommandDecl_MultiArgs(t *testing.T) {
|
||||
rtype := reflect.TypeOf((*testRpcInterfaceForDecls)(nil)).Elem()
|
||||
method, ok := rtype.MethodByName("TwoArgCommand")
|
||||
if !ok {
|
||||
t.Fatalf("TwoArgCommand method not found")
|
||||
}
|
||||
decl := generateWshCommandDecl(method)
|
||||
if decl.Command != "twoarg" {
|
||||
t.Fatalf("expected command twoarg, got %q", decl.Command)
|
||||
}
|
||||
if len(decl.CommandDataTypes) != 2 {
|
||||
t.Fatalf("expected 2 command data types, got %d", len(decl.CommandDataTypes))
|
||||
}
|
||||
if decl.CommandDataTypes[0].Kind() != reflect.String || decl.CommandDataTypes[1].Kind() != reflect.Int {
|
||||
t.Fatalf("unexpected command data types: %#v", decl.CommandDataTypes)
|
||||
}
|
||||
if len(decl.GetCommandDataTypes()) != 2 {
|
||||
t.Fatalf("expected helper to return two command data types")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateWshCommandDeclMap_TestMultiArgCommand(t *testing.T) {
|
||||
decl := GenerateWshCommandDeclMap()["testmultiarg"]
|
||||
if decl == nil {
|
||||
t.Fatalf("expected testmultiarg command declaration")
|
||||
}
|
||||
if decl.MethodName != "TestMultiArgCommand" {
|
||||
t.Fatalf("expected TestMultiArgCommand method name, got %q", decl.MethodName)
|
||||
}
|
||||
if len(decl.GetCommandDataTypes()) != 3 {
|
||||
t.Fatalf("expected 3 command args, got %d", len(decl.GetCommandDataTypes()))
|
||||
}
|
||||
}
|
||||
|
|
@ -23,10 +23,14 @@ type RespOrErrorUnion[T any] struct {
|
|||
Error error
|
||||
}
|
||||
|
||||
type MultiArg struct {
|
||||
Args []any `json:"args"`
|
||||
}
|
||||
|
||||
// Instructions for adding a new RPC call
|
||||
// * methods must end with Command
|
||||
// * methods must take context as their first parameter
|
||||
// * methods may take up to one parameter, and may return either just an error, or one return value plus an error
|
||||
// * methods may take additional typed parameters, and may return either just an error, or one return value plus an error
|
||||
// * after modifying WshRpcInterface, run `task generate` to regnerate bindings
|
||||
|
||||
type WshRpcInterface interface {
|
||||
|
|
@ -69,6 +73,7 @@ type WshRpcInterface interface {
|
|||
StreamWaveAiCommand(ctx context.Context, request WaveAIStreamRequest) chan RespOrErrorUnion[WaveAIPacketType]
|
||||
StreamCpuDataCommand(ctx context.Context, request CpuDataRequest) chan RespOrErrorUnion[TimeSeriesData]
|
||||
TestCommand(ctx context.Context, data string) error
|
||||
TestMultiArgCommand(ctx context.Context, arg1 string, arg2 int, arg3 bool) (string, error)
|
||||
SetConfigCommand(ctx context.Context, data MetaSettingsType) error
|
||||
SetConnectionsConfigCommand(ctx context.Context, data ConnConfigRequest) error
|
||||
GetFullConfigCommand(ctx context.Context) (wconfig.FullConfigType, error)
|
||||
|
|
@ -895,13 +900,13 @@ type BlockJobStatusData struct {
|
|||
}
|
||||
|
||||
type FocusedBlockData struct {
|
||||
BlockId string `json:"blockid"`
|
||||
ViewType string `json:"viewtype"`
|
||||
Controller string `json:"controller"`
|
||||
ConnName string `json:"connname"`
|
||||
BlockMeta waveobj.MetaMapType `json:"blockmeta"`
|
||||
TermJobStatus *BlockJobStatusData `json:"termjobstatus,omitempty"`
|
||||
ConnStatus *ConnStatus `json:"connstatus,omitempty"`
|
||||
TermShellIntegrationStatus string `json:"termshellintegrationstatus,omitempty"`
|
||||
TermLastCommand string `json:"termlastcommand,omitempty"`
|
||||
BlockId string `json:"blockid"`
|
||||
ViewType string `json:"viewtype"`
|
||||
Controller string `json:"controller"`
|
||||
ConnName string `json:"connname"`
|
||||
BlockMeta waveobj.MetaMapType `json:"blockmeta"`
|
||||
TermJobStatus *BlockJobStatusData `json:"termjobstatus,omitempty"`
|
||||
ConnStatus *ConnStatus `json:"connstatus,omitempty"`
|
||||
TermShellIntegrationStatus string `json:"termshellintegrationstatus,omitempty"`
|
||||
TermLastCommand string `json:"termlastcommand,omitempty"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,16 @@ func (ws *WshServer) TestCommand(ctx context.Context, data string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ws *WshServer) TestMultiArgCommand(ctx context.Context, arg1 string, arg2 int, arg3 bool) (string, error) {
|
||||
defer func() {
|
||||
panichandler.PanicHandler("TestMultiArgCommand", recover())
|
||||
}()
|
||||
rpcSource := wshutil.GetRpcSourceFromContext(ctx)
|
||||
rtn := fmt.Sprintf("src:%s arg1:%q arg2:%d arg3:%t", rpcSource, arg1, arg2, arg3)
|
||||
log.Printf("TESTMULTI %s\n", rtn)
|
||||
return rtn, nil
|
||||
}
|
||||
|
||||
// for testing
|
||||
func (ws *WshServer) MessageCommand(ctx context.Context, data wshrpc.CommandMessageData) error {
|
||||
log.Printf("MESSAGE: %s\n", data.Message)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
)
|
||||
|
||||
var WshCommandDeclMap = wshrpc.GenerateWshCommandDeclMap()
|
||||
var multiArgRType = reflect.TypeOf(wshrpc.MultiArg{})
|
||||
|
||||
func findCmdMethod(impl any, cmd string) *reflect.Method {
|
||||
rtype := reflect.TypeOf(impl)
|
||||
|
|
@ -53,19 +54,15 @@ func noImplHandler(handler *RpcResponseHandler) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func recodeCommandData(command string, data any) (any, error) {
|
||||
// only applies to initial command packet
|
||||
if command == "" {
|
||||
func recodeCommandData(command string, data any, commandDataType reflect.Type) (any, error) {
|
||||
if command == "" || commandDataType == nil {
|
||||
return data, nil
|
||||
}
|
||||
methodDecl := WshCommandDeclMap[command]
|
||||
if methodDecl == nil {
|
||||
return data, fmt.Errorf("command %q not found", command)
|
||||
}
|
||||
if methodDecl.CommandDataType == nil {
|
||||
return data, nil
|
||||
}
|
||||
commandDataPtr := reflect.New(methodDecl.CommandDataType).Interface()
|
||||
commandDataPtr := reflect.New(commandDataType).Interface()
|
||||
if data != nil {
|
||||
err := utilfn.ReUnmarshal(commandDataPtr, data)
|
||||
if err != nil {
|
||||
|
|
@ -103,13 +100,37 @@ func serverImplAdapter(impl any) func(*RpcResponseHandler) bool {
|
|||
implMethod := reflect.ValueOf(impl).MethodByName(rmethod.Name)
|
||||
var callParams []reflect.Value
|
||||
callParams = append(callParams, reflect.ValueOf(handler.Context()))
|
||||
if methodDecl.CommandDataType != nil {
|
||||
cmdData, err := recodeCommandData(cmd, handler.GetCommandRawData())
|
||||
commandDataTypes := methodDecl.GetCommandDataTypes()
|
||||
if len(commandDataTypes) == 1 {
|
||||
cmdData, err := recodeCommandData(cmd, handler.GetCommandRawData(), commandDataTypes[0])
|
||||
if err != nil {
|
||||
handler.SendResponseError(err)
|
||||
return true
|
||||
}
|
||||
callParams = append(callParams, reflect.ValueOf(cmdData))
|
||||
} else if len(commandDataTypes) > 1 {
|
||||
multiArgAny, err := recodeCommandData(cmd, handler.GetCommandRawData(), multiArgRType)
|
||||
if err != nil {
|
||||
handler.SendResponseError(err)
|
||||
return true
|
||||
}
|
||||
multiArg, ok := multiArgAny.(wshrpc.MultiArg)
|
||||
if !ok {
|
||||
handler.SendResponseError(fmt.Errorf("command %q invalid multi arg payload", cmd))
|
||||
return true
|
||||
}
|
||||
if len(multiArg.Args) != len(commandDataTypes) {
|
||||
handler.SendResponseError(fmt.Errorf("command %q expected %d args, got %d", cmd, len(commandDataTypes), len(multiArg.Args)))
|
||||
return true
|
||||
}
|
||||
for idx, commandDataType := range commandDataTypes {
|
||||
cmdData, err := recodeCommandData(cmd, multiArg.Args[idx], commandDataType)
|
||||
if err != nil {
|
||||
handler.SendResponseError(err)
|
||||
return true
|
||||
}
|
||||
callParams = append(callParams, reflect.ValueOf(cmdData))
|
||||
}
|
||||
}
|
||||
if methodDecl.CommandType == wshrpc.RpcType_Call {
|
||||
rtnVals := implMethod.Call(callParams)
|
||||
|
|
|
|||
Loading…
Reference in a new issue