Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add include path for idl #14

Merged
merged 4 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ Output:
- `-help` or `-h`: Outputs the usage instructions.
- `-type` or `-t`: Specifies the IDL type: thrift or protobuf. It supports inference based on the IDL file type. The default is thrift..
- `-idl-path` or `-p`: Specifies the path to the IDL file.
- `-include-path`: Add a search path for the IDL. Multiple paths can be added and will be searched in the order they are added.
- `-method` or `-m`: Required, specifies the method name in the format IDLServiceName/MethodName or just MethodName. When the server side has MultiService mode enabled, IDLServiceName must be specified, and the transport protocol must be TTHeader or TTHeaderFramed.
- `-file` or `-f`: Specifies the input file path, which must be in JSON format.
- `-data` or `-d`: Specifies the data to be sent, in JSON string format.
Expand All @@ -118,6 +119,7 @@ Output:
- `-meta`: Specifies one-way metadata passed to the server. Multiple can be specified, in the format key=value.
- `-meta-persistent`: Specifies persistent metadata passed to the server. Multiple can be specified, in the format key=value.
- `-meta-backward`: Enables receiving backward metadata (Backward) returned by the server.
- `-q`: Only output JSON response, no other information.
- `-verbose` or `-v`: Enables verbose mode for more detailed output information.

### Detailed Description
Expand Down
2 changes: 2 additions & 0 deletions README_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ kitexcall -idl-path echo.thrift -m echo -d '{"message": "hello"}' -e 127.0.0.1:9
- `-help` 或 `-h`:输出使用说明。
- `-type` 或 `-t`:指定 IDL 类型:`thrift` 或 `protobuf`,支持通过 IDL 文件类型推测,默认是 `thrift`。
- `-idl-path` 或 `-p`:指定 IDL 文件的路径。
- `-include-path`:添加一个 IDL 里 include 的其他文件的搜索路径。支持添加多个,会按照添加的路径顺序搜索。
- `-method` 或 `-m`:必选,指定方法名,格式为 `IDLServiceName/MethodName` 或仅为 `MethodName`。当 server 端开启了 MultiService 模式时,必须指定 `IDLServiceName`,同时指定传输协议为 TTHeader 或 TTHeaderFramed 。
- `-file` 或 `-f`:指定输入文件路径,必须是 JSON 格式。
- `-data` 或 `-d`:指定要发送的数据,格式为 JSON 字符串。
Expand All @@ -120,6 +121,7 @@ kitexcall -idl-path echo.thrift -m echo -d '{"message": "hello"}' -e 127.0.0.1:9
- `-meta`:指定传递给 server 的单跳透传元信息。可以指定多个,格式为 key=value。
- `-meta-persistent`:指定传递给 server 的持续透传元信息。可以指定多个,格式为 key=value。
- `-meta-backward`:启用从服务器接收反向透传元信息。
- `-q`: 只输出Json响应,不输出其他提示信息。
- `-verbose` 或 `-v`:启用详细模式。

### 详细描述
Expand Down
89 changes: 81 additions & 8 deletions internal/test/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"encoding/json"
"errors"
"net"
"os"
"path/filepath"
"testing"
"time"

Expand All @@ -38,14 +40,17 @@ import (
)

var (
thriftGenericServer server.Server
pbGenericServer server.Server
bizErrorGenericServer server.Server
thriftServerHost = "127.0.0.1:9919"
pbServerHostPort = "127.0.0.1:9199"
bizErrorServerHost = "127.0.0.1:9109"
pbFilePath = "./example_service.proto"
thriftFilePath = "./example_service.thrift"
thriftGenericServer server.Server
pbGenericServer server.Server
bizErrorGenericServer server.Server
thriftGenericServerWithImportPath server.Server
thriftServerHost = "127.0.0.1:9919"
pbServerHostPort = "127.0.0.1:9199"
bizErrorServerHost = "127.0.0.1:9109"
pbFilePath = "./example_service.proto"
thriftFilePath = "./example_service.thrift"
example2ServerHost = "127.0.0.1:9100"
example2FilePath = "./example2.thrift"
)

func InitPbGenericServer() {
Expand Down Expand Up @@ -100,6 +105,30 @@ func InitThriftGenericServer() {
WaitServerStart(thriftServerHost)
}

func InitThriftGenericServerWithImportPath() {
p, err := generic.NewThriftFileProvider(example2FilePath, "./idl")
if err != nil {
panic(err)
}
g, err := generic.JSONThriftGeneric(p)
if err != nil {
panic(err)
}

go func() {
addr, _ := net.ResolveTCPAddr("tcp", example2ServerHost)
klog.Infof("Starting Example2 service on %s", addr.String())

thriftGenericServerWithImportPath = genericserver.NewServer(new(GenericServiceImpl), g, server.WithServiceAddr(addr))

if err := thriftGenericServerWithImportPath.Run(); err != nil {
klog.Fatalf("Failed to run Example2 service: %v", err)
}
}()

WaitServerStart(example2ServerHost)
}

// WaitServerStart waits for server to start for at most 1 second
func WaitServerStart(addr string) {
for begin := time.Now(); time.Since(begin) < time.Second; {
Expand Down Expand Up @@ -292,3 +321,47 @@ func TestBizErrorGenericServer_invokeRPC(t *testing.T) {
t.Errorf("Expected BizMessage %s, got %s", expectedMessage, bizErr.BizMessage())
}
}

func TestExample2Service_withImportPath(t *testing.T) {
InitThriftGenericServerWithImportPath()
defer thriftGenericServerWithImportPath.Stop()

currentPath, _ := os.Getwd()
includePath := filepath.Join(currentPath, "idl")

conf := &config.Config{
Type: config.Thrift,
Endpoint: []string{example2ServerHost},
IDLPath: example2FilePath,
IDLServiceName: "Example2Service",
Method: "Example2Method",
Data: "{\"Msg\": \"hello\", \"User\": {\"UserID\": \"123\", \"UserName\": \"Alice\", \"Age\": 25}}",
Transport: config.TTHeader,
MetaBackward: true,
IncludePath: []string{includePath},
}

cli, err := client.InvokeRPC(conf)
if err != nil {
t.Fatalf("InvokeRPC failed: %v", err)
}

resp := cli.GetResponse()
if resp == nil {
t.Fatalf("Response is nil")
}

expectedResponse := `{"Msg":"world", "BaseResp": {"StatusCode": 0, "StatusMessage": ""}}`

var serverData, expectedData interface{}
json.Unmarshal([]byte(resp.(string)), &serverData)
json.Unmarshal([]byte(expectedResponse), &expectedData)
DeepEqual(t, serverData, expectedData)

// MetaBackward
if conf.MetaBackward {
if res := cli.GetMetaBackward(); res == nil {
t.Errorf("Expected meta backward not found in response")
}
}
}
34 changes: 34 additions & 0 deletions internal/test/example2.thrift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2024 CloudWeGo 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.

namespace go kitex.test.server

include "common.thrift"

// Request data for Example2Service
struct Example2Req {
1: required string Msg,
2: required common.UserData User,
}

// Response for Example2Service
struct Example2Resp {
1: required string Msg,
2: required common.CommonResponse BaseResp,
}

// Service definition that uses imported common types
service Example2Service {
Example2Resp Example2Method(1: Example2Req req),
}
29 changes: 29 additions & 0 deletions internal/test/idl/common.thrift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2024 CloudWeGo 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.

namespace go kitex.test.common

// Commonly shared struct in multiple services
struct CommonResponse {
1: string StatusMessage = "",
2: i32 StatusCode = 0,
3: optional map<string, string> Extra,
}

// Common data shared across services
struct UserData {
1: string UserID,
2: string UserName,
3: i32 Age,
}
25 changes: 18 additions & 7 deletions pkg/argparse/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func NewArgument() *Argument {
}
}

type EndpointList []string
type StringList []string

func (e *EndpointList) String() string {
return strings.Join(*e, ",")
func (s *StringList) String() string {
return strings.Join(*s, ",")
}

func (e *EndpointList) Set(value string) error {
*e = append(*e, value)
func (s *StringList) Set(value string) error {
*s = append(*s, value)
return nil
}

Expand Down Expand Up @@ -90,8 +90,8 @@ func (a *Argument) buildFlags() *flag.FlagSet {
f.StringVar(&a.Data, "data", "", "Specify the data to be sent as a JSON formatted string.")
f.StringVar(&a.Data, "d", "", "Specify the data to be sent as a JSON formatted string. (shorthand)")

f.Var((*EndpointList)(&a.Endpoint), "endpoint", "Specify the server endpoints. Can be repeated.")
f.Var((*EndpointList)(&a.Endpoint), "e", "Specify the server endpoints. Can be repeated. (shorthand)")
f.Var((*StringList)(&a.Endpoint), "endpoint", "Specify the server endpoints. Can be repeated.")
f.Var((*StringList)(&a.Endpoint), "e", "Specify the server endpoints. Can be repeated. (shorthand)")

f.BoolVar(&a.Verbose, "verbose", false, "Enable verbose mode.")
f.BoolVar(&a.Verbose, "v", false, "Enable verbose mode. (shorthand)")
Expand All @@ -109,6 +109,8 @@ func (a *Argument) buildFlags() *flag.FlagSet {

f.BoolVar(&a.Quiet, "quiet", false, "Enable only print rpc response.")

f.Var((*StringList)(&a.IncludePath), "include-path", "Specify additional paths for imported IDL files. Can be repeated.")

return f
}

Expand Down Expand Up @@ -242,6 +244,14 @@ func (a *Argument) checkIDL() error {
return errors.New(errors.ArgParseError, "IDL file does not exist")
}

if a.IncludePath != nil {
for _, path := range a.IncludePath {
if _, err := os.Stat(path); os.IsNotExist(err) {
return errors.New(errors.ArgParseError, "Include path does not exist")
}
}
}

switch a.Type {
case "thrift", "protobuf":
case "unknown":
Expand Down Expand Up @@ -282,6 +292,7 @@ func (a *Argument) BuildConfig() *config.Config {
MetaBackward: a.MetaBackward,
BizError: a.BizError,
Quiet: a.Quiet,
IncludePath: a.IncludePath,
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/generic_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func NewThriftGeneric() *ThriftGeneric {

func (c *ThriftGeneric) Init(Conf *config.Config) error {
// Waiting for server reflection
p, err := generic.NewThriftFileProvider(Conf.IDLPath)
p, err := generic.NewThriftFileProvider(Conf.IDLPath, Conf.IncludePath...)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Config struct {
MetaBackward bool
BizError bool
Quiet bool
IncludePath []string
}

type ConfigBuilder interface {
Expand Down
Loading