Skip to content

Commit

Permalink
feat: add kitexcall
Browse files Browse the repository at this point in the history
  • Loading branch information
Zzhiter committed May 7, 2024
1 parent 47b6250 commit f7abf60
Show file tree
Hide file tree
Showing 16 changed files with 1,202 additions and 247 deletions.
55 changes: 53 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
module github.com/kitex-contrib/kitexcall

go 1.16
go 1.21

require github.com/cloudwego/kitex v0.9.1
toolchain go1.21.9

require (
github.com/bytedance/gopkg v0.0.0-20230728082804-614d0af6619b
github.com/cloudwego/dynamicgo v0.2.0
github.com/cloudwego/kitex v0.9.1
)

require (
github.com/apache/thrift v0.16.0 // indirect
github.com/bytedance/sonic v1.11.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/choleraehyq/pid v0.0.18 // indirect
github.com/cloudwego/configmanager v0.2.0 // indirect
github.com/cloudwego/fastpb v0.0.4 // indirect
github.com/cloudwego/frugal v0.1.14 // indirect
github.com/cloudwego/localsession v0.0.2 // indirect
github.com/cloudwego/netpoll v0.6.0 // indirect
github.com/cloudwego/thriftgo v0.3.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20230509042627-b1315fad0c5a // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/jhump/protoreflect v1.8.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/gls v0.0.0-20220109145502-612d0167dce5 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oleiade/lane v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.2.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
55 changes: 39 additions & 16 deletions go.sum

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions internal/test/assert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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.
*/

package test

import (
"reflect"
"testing"
)

// DeepEqual asserts a and b are deep equal, otherwise fails the test.
func DeepEqual(t *testing.T, a, b interface{}) {
t.Helper()
if !reflect.DeepEqual(a, b) {
t.Fatalf("assertion failed: %v != %v", a, b)
}
}
295 changes: 295 additions & 0 deletions internal/test/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
/*
* 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.
*/

package test

import (
"context"
"encoding/json"
"errors"
"net"
"testing"
"time"

"github.com/bytedance/gopkg/cloud/metainfo"
dproto "github.com/cloudwego/dynamicgo/proto"

"github.com/cloudwego/kitex/pkg/generic"
"github.com/cloudwego/kitex/pkg/kerrors"
"github.com/cloudwego/kitex/pkg/klog"
"github.com/cloudwego/kitex/pkg/transmeta"
"github.com/cloudwego/kitex/server"
"github.com/cloudwego/kitex/server/genericserver"
"github.com/kitex-contrib/kitexcall/pkg/client"
"github.com/kitex-contrib/kitexcall/pkg/config"
)

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"
)

func InitPbGenericServer() {
dOpts := dproto.Options{}
p, err := generic.NewPbFileProviderWithDynamicGo(pbFilePath, context.Background(), dOpts)
if err != nil {
panic(err)
}

g, err := generic.JSONPbGeneric(p)
if err != nil {
panic(err)
}

go func() {
var opts []server.Option
addr, _ := net.ResolveTCPAddr("tcp", pbServerHostPort)
opts = append(opts, server.WithServiceAddr(addr))

pbGenericServer = genericserver.NewServer(new(GenericServiceImpl), g, opts...)
klog.Infof("Starting pb generic server on %s", addr.String())

if err := pbGenericServer.Run(); err != nil {
klog.Infof(err.Error())
}
}()

WaitServerStart(pbServerHostPort)
}

func InitThriftGenericServer() {
p, err := generic.NewThriftFileProvider(thriftFilePath)
if err != nil {
panic(err)
}
g, err := generic.JSONThriftGeneric(p)
if err != nil {
panic(err)
}

go func() {
addr, _ := net.ResolveTCPAddr("tcp", thriftServerHost)
klog.Infof("Starting thrift generic server on %s", addr.String())

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

if err := thriftGenericServer.Run(); err != nil {
klog.Fatalf("Failed to run generic server: %v", err)
}
}()

WaitServerStart(thriftServerHost)
}

// WaitServerStart waits for server to start for at most 1 second
func WaitServerStart(addr string) {
for begin := time.Now(); time.Since(begin) < time.Second; {
if _, err := net.Dial("tcp", addr); err == nil {
klog.Infof("server is up at %s", addr)
return
}
time.Sleep(time.Millisecond * 10)
}
}

type GenericServiceImpl struct{}

func (g *GenericServiceImpl) GenericCall(ctx context.Context, method string, request interface{}) (response interface{}, err error) {
temp, ok1 := metainfo.GetValue(ctx, "temp")
if ok1 {
klog.Info(temp)
} else {
klog.Warn("`temp` not exist in server-1 context")
}

logid, ok2 := metainfo.GetPersistentValue(ctx, "logid")
if ok2 {
klog.Info(logid)
} else {
klog.Warn("`logid` not exist in server-1 context")
}

ok := metainfo.SendBackwardValue(ctx, "something-from-server", time.Now().String())
if !ok {
return nil, errors.New("it looks like the protocol does not support transmitting meta information backward")
}

return "{\"Msg\": \"world\"}", nil
}

type BizErrorServiceImpl struct{}

func (g *BizErrorServiceImpl) GenericCall(ctx context.Context, method string, request interface{}) (response interface{}, err error) {
err = kerrors.NewBizStatusError(404, "not found")
return nil, err
}

func InitBizErrorGenericServer() {
p, err := generic.NewThriftFileProvider(thriftFilePath)
if err != nil {
panic(err)
}
g, err := generic.JSONThriftGeneric(p)
if err != nil {
panic(err)
}

go func() {
addr, _ := net.ResolveTCPAddr("tcp", bizErrorServerHost)
klog.Infof("Starting thrift generic server on %s", addr.String())

bizErrorGenericServer = genericserver.NewServer(new(BizErrorServiceImpl), g, server.WithServiceAddr(addr),
server.WithMetaHandler(transmeta.ServerTTHeaderHandler))

if err := bizErrorGenericServer.Run(); err != nil {
klog.Fatalf("Failed to run generic server: %v", err)
}
}()

WaitServerStart(bizErrorServerHost)
}

func TestThriftGenericServer_invokeRPC(t *testing.T) {
InitThriftGenericServer()
defer thriftGenericServer.Stop()

conf := &config.Config{
Type: config.Thrift,
Endpoint: []string{thriftServerHost},
IDLPath: thriftFilePath,
Service: "GenericService",
Method: "ExampleMethod",
Data: "{\"Msg\": \"hello\"}",
Transport: config.TTHeader,
MetaBackward: map[string]string{"something-from-server": ""},
Meta: map[string]string{"temp": "temp-value"},
MetaPersistent: map[string]string{"logid": "12345"},
}

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 len(conf.MetaBackward) > 0 {
if _, ok := conf.MetaBackward["something-from-server"]; !ok {
t.Errorf("Expected meta backward key 'something-from-server' not found in response")
}
}
}

func TestPbGenericServer_invokeRPC(t *testing.T) {
InitPbGenericServer()
defer pbGenericServer.Stop()

conf := &config.Config{
Type: config.Protobuf,
Endpoint: []string{pbServerHostPort},
IDLPath: pbFilePath,
Service: "GenericService",
Method: "ExampleMethod",
Data: "{\"Msg\": \"hello\"}",
Transport: config.TTHeader,
MetaBackward: map[string]string{"something-from-server": ""},
Meta: map[string]string{"temp": "temp-value"},
MetaPersistent: map[string]string{"logid": "12345"},
}

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"}`

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

// MetaBackward
if len(conf.MetaBackward) > 0 {
if _, ok := conf.MetaBackward["something-from-server"]; !ok {
t.Errorf("Expected meta backward key 'something-from-server' not found in response")
}
}
}

func TestBizErrorGenericServer_invokeRPC(t *testing.T) {
InitBizErrorGenericServer()
defer bizErrorGenericServer.Stop()

conf := &config.Config{
Type: config.Thrift,
Endpoint: []string{bizErrorServerHost},
IDLPath: thriftFilePath,
Service: "GenericService",
Method: "ExampleMethod",
Transport: config.TTHeader,
BizError: true,
}

c := client.NewThriftGeneric()

if err := c.Init(conf); err != nil {
t.Fatalf("Client init failed: %v", err)
}

err := c.Call()
if err == nil {
t.Errorf("Expected error, got nil")
}

// Handle Biz error
bizErr, isBizErr := kerrors.FromBizStatusError(err)
if !isBizErr {
t.Errorf("Expected BizStatusError, got %v", err)
}

expectedCode := int32(404)
expectedMessage := "not found"
if bizErr.BizStatusCode() != expectedCode {
t.Errorf("Expected BizStatusCode %d, got %d", expectedCode, bizErr.BizStatusCode())
}
if bizErr.BizMessage() != expectedMessage {
t.Errorf("Expected BizMessage %s, got %s", expectedMessage, bizErr.BizMessage())
}

}
Loading

0 comments on commit f7abf60

Please sign in to comment.