diff --git a/Makefile b/Makefile index 362c274..b7e6841 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ BINARY=ptg GOBUILD=go build -ldflags "-s -w" -o ${BINARY} GOCLEAN=go clean RMTARGZ=rm -rf *.gz -VERSION=0.0.2 +VERSION=1.0.1 # Build build: @@ -47,6 +47,11 @@ gen-go-proto: --go-grpc_out=. --go-grpc_opt=paths=source_relative \ reflect/gen/user.proto +gen-log-proto: + @protoc --go_out=. --go_opt=paths=source_relative \ + --go-grpc_out=. --go-grpc_opt=paths=source_relative \ + gui/io/log.proto + pkg-win: fyne package -os windows -src gui/ -icon pic/gopher.png -name ${BINARY} -appVersion $(VERSION) diff --git a/README.md b/README.md index eda99a3..dae8d43 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,20 @@ Performance testing tool (Go), It is also a **GUI** `gRPC` client. Test the `gRPC` service like `postman`. ![](pic/show.gif) -![](pic/ptg.gif) +![](pic/ptg-min.gif) + + +# Features +- [x] Cli performance test support. +- [x] GUI support. +- [x] Metadata support. +- [x] Data persistence. +- [ ] Stream call. +- [ ] Benchmark GUI. # Install -## cli app +## Cli app ```go go get github.com/crossoverJie/ptg ``` @@ -93,11 +102,3 @@ Number of Errors: 0 ``` -# TODO - -- [x] cli support. -- [x] GUI support. -- [ ] metadata support. -- [ ] stream call. -- [ ] Data persistence. -- [ ] benchmark GUI. \ No newline at end of file diff --git a/gui/app.go b/gui/app.go index a7592e1..ce1413e 100644 --- a/gui/app.go +++ b/gui/app.go @@ -1,14 +1,18 @@ package main const ( - AppName = "PTG gRPC client" - AppWeight = 1000 - AppHeight = 500 - HelpUrl = "https://github.com/crossoverJie/ptg" - RequestEntryPlaceHolder = "Input request json" - TargetInputText = "127.0.0.1:6001" - RequestButtonText = "RUN" - ResponseLabelText = "Response:" + AppName = "PTG gRPC client" + AppWeight = 1000 + AppHeight = 500 + HelpUrl = "https://github.com/crossoverJie/ptg" + TargetFormText = "Target:" + TargetFormHintText = "Input target url" + RequestEntryPlaceHolder = "Input request json" + MetaDataAccordion = "metadata" + MetaDataInputPlaceHolder = "Input metadata json" + TargetInputText = "127.0.0.1:6001" + RequestButtonText = "RUN" + ResponseLabelText = "Response:" ) type App struct { @@ -20,8 +24,10 @@ type App struct { } type RightRequest struct { - RequestEntryPlaceHolder, TargetInputText string - RequestButtonText string + TargetFormText, TargetFormHintText string + RequestEntryPlaceHolder, TargetInputText string + MetaDataAccordionTitle, MetaDataInputPlaceHolder string + RequestButtonText string } type RightResponse struct { @@ -35,9 +41,13 @@ func InitApp() *App { AppHeight: AppHeight, HelpUrl: HelpUrl, RightRequest: &RightRequest{ - RequestEntryPlaceHolder: RequestEntryPlaceHolder, - TargetInputText: TargetInputText, - RequestButtonText: RequestButtonText, + TargetFormText: TargetFormText, + TargetFormHintText: TargetFormHintText, + RequestEntryPlaceHolder: RequestEntryPlaceHolder, + TargetInputText: TargetInputText, + MetaDataAccordionTitle: MetaDataAccordion, + MetaDataInputPlaceHolder: MetaDataInputPlaceHolder, + RequestButtonText: RequestButtonText, }, RightResponse: &RightResponse{ResponseLabelText: ResponseLabelText}, } diff --git a/gui/io/log.pb.go b/gui/io/log.pb.go new file mode 100644 index 0000000..4452b8c --- /dev/null +++ b/gui/io/log.pb.go @@ -0,0 +1,182 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.5.1 +// source: gui/io/log.proto + +package io + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Filenames []string `protobuf:"bytes,1,rep,name=filenames,proto3" json:"filenames,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Request string `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + Metadata string `protobuf:"bytes,4,opt,name=metadata,proto3" json:"metadata,omitempty"` + Response string `protobuf:"bytes,5,opt,name=response,proto3" json:"response,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_gui_io_log_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_gui_io_log_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_gui_io_log_proto_rawDescGZIP(), []int{0} +} + +func (x *Log) GetFilenames() []string { + if x != nil { + return x.Filenames + } + return nil +} + +func (x *Log) GetTarget() string { + if x != nil { + return x.Target + } + return "" +} + +func (x *Log) GetRequest() string { + if x != nil { + return x.Request + } + return "" +} + +func (x *Log) GetMetadata() string { + if x != nil { + return x.Metadata + } + return "" +} + +func (x *Log) GetResponse() string { + if x != nil { + return x.Response + } + return "" +} + +var File_gui_io_log_proto protoreflect.FileDescriptor + +var file_gui_io_log_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x67, 0x75, 0x69, 0x2f, 0x69, 0x6f, 0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x02, 0x69, 0x6f, 0x22, 0x8d, 0x01, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x1c, + 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x6f, 0x76, 0x65, 0x72, 0x4a, 0x69, + 0x65, 0x2f, 0x70, 0x74, 0x67, 0x2f, 0x67, 0x75, 0x69, 0x2f, 0x69, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_gui_io_log_proto_rawDescOnce sync.Once + file_gui_io_log_proto_rawDescData = file_gui_io_log_proto_rawDesc +) + +func file_gui_io_log_proto_rawDescGZIP() []byte { + file_gui_io_log_proto_rawDescOnce.Do(func() { + file_gui_io_log_proto_rawDescData = protoimpl.X.CompressGZIP(file_gui_io_log_proto_rawDescData) + }) + return file_gui_io_log_proto_rawDescData +} + +var file_gui_io_log_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_gui_io_log_proto_goTypes = []interface{}{ + (*Log)(nil), // 0: io.Log +} +var file_gui_io_log_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_gui_io_log_proto_init() } +func file_gui_io_log_proto_init() { + if File_gui_io_log_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_gui_io_log_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_gui_io_log_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_gui_io_log_proto_goTypes, + DependencyIndexes: file_gui_io_log_proto_depIdxs, + MessageInfos: file_gui_io_log_proto_msgTypes, + }.Build() + File_gui_io_log_proto = out.File + file_gui_io_log_proto_rawDesc = nil + file_gui_io_log_proto_goTypes = nil + file_gui_io_log_proto_depIdxs = nil +} diff --git a/gui/io/log.proto b/gui/io/log.proto new file mode 100644 index 0000000..b0f0080 --- /dev/null +++ b/gui/io/log.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +option go_package = "github.com/crossoverJie/ptg/gui/io"; + +package io; + +message Log{ + repeated string filenames = 1; + string target = 2; + string request = 3; + string metadata = 4; + string response = 5; +} \ No newline at end of file diff --git a/gui/io/serialize.go b/gui/io/serialize.go new file mode 100644 index 0000000..91a3400 --- /dev/null +++ b/gui/io/serialize.go @@ -0,0 +1,61 @@ +package io + +import ( + "github.com/golang/protobuf/proto" + "io/ioutil" + "os" + "os/user" +) + +func SaveLog(data []byte) error { + filename, err := initLog() + if err != nil { + return err + } + + return ioutil.WriteFile(filename, data, 0666) +} + +func initLog() (string, error) { + home, err := user.Current() + if err != nil { + return "", err + } + + filename := home.HomeDir + "/.ptg/ptg.log" + + if !exist(filename) { + err := os.MkdirAll(home.HomeDir+"/.ptg/", 0777) + if err != nil { + return "", err + } + _, err = os.Create(filename) + if err != nil { + return "", err + } + } + return filename, nil +} + +func LoadLog() ([]byte, error) { + filename, err := initLog() + if err != nil { + return nil, err + } + return ioutil.ReadFile(filename) +} + +func LoadLogWithStruct() (*Log, error) { + bytes, err := LoadLog() + var read Log + err = proto.Unmarshal(bytes, &read) + if err != nil { + return nil, err + } + return &read, nil +} + +func exist(filename string) bool { + _, err := os.Stat(filename) + return err == nil || os.IsExist(err) +} diff --git a/gui/io/serialize_test.go b/gui/io/serialize_test.go new file mode 100644 index 0000000..71f69f2 --- /dev/null +++ b/gui/io/serialize_test.go @@ -0,0 +1,47 @@ +package io + +import ( + "fmt" + "github.com/golang/protobuf/proto" + "testing" +) + +func TestSaveLog(t *testing.T) { + creat := Log{ + Filenames: []string{"test.proto", "user.proto"}, + Target: "127.0.0.1:6001", + Request: `{"order_id":1123120,"reason_id":null,"remark":"","user_id":null}`, + Metadata: `{"lang":"zh"}`, + Response: `{"orderId":"1123120"}`, + } + marshal, err := proto.Marshal(&creat) + if err != nil { + panic(err) + } + err = SaveLog(marshal) + if err != nil { + panic(err) + } +} + +func TestLoadLog(t *testing.T) { + bytes, err := LoadLog() + if err != nil { + panic(err) + } + + var read Log + err = proto.Unmarshal(bytes, &read) + if err != nil { + panic(err) + } + fmt.Println(read) +} + +func TestLoadLogWithStruct(t *testing.T) { + withStruct, err := LoadLogWithStruct() + if err != nil { + panic(err) + } + fmt.Println(withStruct) +} diff --git a/gui/main.go b/gui/main.go index 6a815fd..e7f2dd9 100644 --- a/gui/main.go +++ b/gui/main.go @@ -13,11 +13,15 @@ import ( "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" + "github.com/crossoverJie/ptg/gui/io" "github.com/crossoverJie/ptg/reflect" _ "github.com/crossoverJie/ptg/reflect" + "github.com/golang/protobuf/proto" "github.com/jhump/protoreflect/dynamic/grpcdynamic" "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "image/color" + gio "io" "net/url" "strings" ) @@ -37,65 +41,70 @@ func main() { targetInput := widget.NewEntry() targetInput.SetText(ptgApp.RightRequest.TargetInputText) targetInput.SetPlaceHolder("") + metadataEntry := widget.NewMultiLineEntry() + metadataEntry.SetPlaceHolder(ptgApp.RightRequest.MetaDataInputPlaceHolder) processBar := widget.NewProgressBarInfinite() processBar.Hide() serviceAccordionRemove := false serviceAccordion := widget.NewAccordion() content := container.NewVBox() - fileOpen := dialog.NewFileOpen(func(uri fyne.URIReadCloser, err error) { + newProto := func(uri fyne.URIReadCloser, err error) { if err != nil { dialog.ShowError(err, window) return } - if uri != nil { - parseAdapter, exit, err := RegisterReflect(uri.URI().Path()) - if err != nil { - dialog.ShowError(err, window) - return - } - if exit { - dialog.ShowError(errors.New("proto file already exists"), window) - return - } + if uri == nil { + return + } - maps := parseAdapter.Parse().ServiceInfoMaps() - if serviceAccordionRemove { - content.Add(serviceAccordion) - serviceAccordionRemove = false - } - for k, v := range maps { - var methods []string - for _, s := range v { - methods = append(methods, k+"."+s+"-"+fmt.Sprint(parseAdapter.Index())) - } - serviceAccordion.Append(&widget.AccordionItem{ - Title: k, - Detail: widget.NewRadioGroup(methods, func(s string) { - if s == "" { - return - } - methodInfo := strings.Split(s, "-") - service, method, err := reflect.ParseServiceMethod(methodInfo[0]) - if err != nil { - dialog.ShowError(err, window) - return - } - json, err := GetParseAdapter(methodInfo[1]).Parse().RequestJSON(service, method) - if err != nil { - dialog.ShowError(err, window) - return - } - requestEntry.SetText(json) - reqLabel.SetText(s) - - }), - Open: false, - }) + parseAdapter, exit, err := RegisterReflect(uri.URI().Path()) + if err != nil { + dialog.ShowError(err, window) + return + } + if exit { + dialog.ShowError(errors.New("proto file already exists"), window) + return + } + maps := parseAdapter.Parse().ServiceInfoMaps() + if serviceAccordionRemove { + content.Add(serviceAccordion) + serviceAccordionRemove = false + } + for k, v := range maps { + var methods []string + for _, s := range v { + methods = append(methods, k+"."+s+"-"+fmt.Sprint(parseAdapter.Index())) } + serviceAccordion.Append(&widget.AccordionItem{ + Title: k, + Detail: widget.NewRadioGroup(methods, func(s string) { + if s == "" { + return + } + methodInfo := strings.Split(s, "-") + service, method, err := reflect.ParseServiceMethod(methodInfo[0]) + if err != nil { + dialog.ShowError(err, window) + return + } + json, err := GetParseAdapter(methodInfo[1]).Parse().RequestJSON(service, method) + if err != nil { + dialog.ShowError(err, window) + return + } + requestEntry.SetText(json) + reqLabel.SetText(s) + + }), + Open: false, + }) } - }, window) + + } + fileOpen := dialog.NewFileOpen(newProto, window) toolbar := widget.NewToolbar( widget.NewToolbarAction(theme.ContentAddIcon(), func() { @@ -127,9 +136,9 @@ func main() { // Right form := widget.NewForm(&widget.FormItem{ - Text: "Target:", + Text: ptgApp.RightRequest.TargetFormText, Widget: targetInput, - HintText: "Input target url", + HintText: ptgApp.RightRequest.TargetFormHintText, }) requestContainer := container.New(layout.NewGridLayoutWithColumns(1)) @@ -159,6 +168,12 @@ func main() { var opts []grpc.DialOption opts = append(opts, grpc.WithInsecure()) ctx := context.Background() + ctx, err = buildWithMetadata(ctx, metadataEntry.Text) + if err != nil { + dialog.ShowError(err, window) + return + } + conn, err := grpc.DialContext(ctx, targetInput.Text, opts...) if err != nil { dialog.ShowError(err, window) @@ -176,7 +191,13 @@ func main() { marshalIndent, _ := json.MarshalIndent(rpc, "", "\t") responseEntry.SetText(string(marshalIndent)) }) - bottomBox := container.NewVBox(canvas.NewLine(color.Black), requestButton) + bottomBox := container.NewVBox(widget.NewAccordion(&widget.AccordionItem{ + Title: ptgApp.RightRequest.MetaDataAccordionTitle, + Detail: metadataEntry, + Open: false, + })) + bottomBox.Add(canvas.NewLine(color.Black)) + bottomBox.Add(requestButton) bottomBox.Add(canvas.NewLine(color.Black)) bottomBox.Add(processBar) requestPanel := container.NewBorder(form, bottomBox, nil, nil) @@ -193,5 +214,117 @@ func main() { split := container.NewHSplit(leftTool, rightTool) window.SetContent(split) + app.Lifecycle().SetOnStarted(func() { + log, err := io.LoadLogWithStruct() + if err != nil { + dialog.ShowError(err, window) + } + for _, filename := range log.Filenames { + newProto(&ResetUri{ + Filename: filename, + }, nil) + } + if log.Target != "" { + targetInput.SetText(log.Target) + } + if log.Request != "" { + requestEntry.SetText(log.Request) + } + if log.Response != "" { + responseEntry.SetText(log.Response) + } + if log.Metadata != "" { + metadataEntry.SetText(log.Metadata) + } + }) + app.Lifecycle().SetOnStopped(func() { + var filenames []string + for filename, _ := range ParseContainer() { + filenames = append(filenames, filename) + } + err := SaveLog(filenames, targetInput.Text, requestEntry.Text, responseEntry.Text, metadataEntry.Text) + if err != nil { + dialog.ShowError(err, window) + } + }) window.ShowAndRun() } + +func buildWithMetadata(ctx context.Context, meta string) (context.Context, error) { + if strings.Trim(meta, "") != "" { + var m map[string]string + err := json.Unmarshal([]byte(meta), &m) + if err != nil { + return nil, err + } + md := metadata.New(m) + ctx := metadata.NewOutgoingContext(ctx, md) + return ctx, nil + } + return ctx, nil + +} + +func SaveLog(filenames []string, target, request, response, metadata string) error { + log := io.Log{ + Filenames: filenames, + Target: target, + Request: request, + Metadata: metadata, + Response: response, + } + marshal, err := proto.Marshal(&log) + if err != nil { + return err + } + return io.SaveLog(marshal) +} + +type ResetUri struct { + gio.ReadCloser + Filename string +} + +func (r *ResetUri) URI() fyne.URI { + return &uri{path: r.Filename} +} + +type uri struct { + path string +} + +func (u *uri) Extension() string { + return "" +} + +func (u *uri) Name() string { + return "" +} + +func (u *uri) MimeType() string { + return "" +} + +func (u *uri) Scheme() string { + return "" +} + +func (u *uri) String() string { + return "" +} + +func (u *uri) Authority() string { + return "" +} + +func (u *uri) Path() string { + return u.path +} + +func (u *uri) Query() string { + return "" +} + +func (u *uri) Fragment() string { + return "" +} diff --git a/gui/reflect.go b/gui/reflect.go index 88a83a9..12bc9b3 100644 --- a/gui/reflect.go +++ b/gui/reflect.go @@ -7,6 +7,7 @@ import ( ) var ( + // filename->*ParseReflectAdapter parseContainerMap map[string]*ParseReflectAdapter // index->filename containerMap map[string]string diff --git a/pic/ptg-min.gif b/pic/ptg-min.gif new file mode 100644 index 0000000..130c819 Binary files /dev/null and b/pic/ptg-min.gif differ diff --git a/pic/ptg.gif b/pic/ptg.gif deleted file mode 100644 index 71a8928..0000000 Binary files a/pic/ptg.gif and /dev/null differ diff --git a/reflect/reflect.go b/reflect/reflect.go index 71b50d3..f34413c 100644 --- a/reflect/reflect.go +++ b/reflect/reflect.go @@ -80,6 +80,7 @@ func (p *ParseReflect) MethodDescriptor(serviceName, methodName string) (*desc.M return sds.FindMethodByName(methodName), nil } +// make unary RPC func (p *ParseReflect) InvokeRpc(ctx context.Context, stub grpcdynamic.Stub, mds *desc.MethodDescriptor, data string, opts ...grpc.CallOption) (proto.Message, error) { messages, err := createPayloadsFromJSON(mds, data) diff --git a/reflect/reflect_test.go b/reflect/reflect_test.go index c67a1ad..2c6ad85 100644 --- a/reflect/reflect_test.go +++ b/reflect/reflect_test.go @@ -8,7 +8,9 @@ import ( "github.com/crossoverJie/ptg/reflect/gen/user" "github.com/jhump/protoreflect/dynamic/grpcdynamic" "google.golang.org/grpc" - "google.golang.org/grpc/reflection" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" "log" "net" "strings" @@ -32,10 +34,6 @@ func TestRequestJSON(t *testing.T) { if err != nil { panic(err) } - if err != nil { - panic(err) - } - json, err := parse.RequestJSON("order.v1.OrderService", "Create") if err != nil { panic(err) @@ -45,11 +43,14 @@ func TestRequestJSON(t *testing.T) { func TestParseReflect_InvokeRpc(t *testing.T) { data := `{"order_id":20,"user_id":[20],"remark":"Hello","reason_id":[10]}` - filename := "gen/test.proto" - parse, err := NewParse(filename) + metaStr := `{"lang":"zh"}` + var m map[string]string + err := json.Unmarshal([]byte(metaStr), &m) if err != nil { panic(err) } + filename := "gen/test.proto" + parse, err := NewParse(filename) if err != nil { panic(err) } @@ -60,9 +61,15 @@ func TestParseReflect_InvokeRpc(t *testing.T) { } var opts []grpc.DialOption opts = append(opts, grpc.WithInsecure()) - conn, err := grpc.DialContext(context.Background(), "127.0.0.1:5000", opts...) + conn, err := grpc.DialContext(context.Background(), "127.0.0.1:6001", opts...) stub := grpcdynamic.NewStub(conn) - rpc, err := parse.InvokeRpc(context.Background(), stub, mds, data) + + // metadata + // create a new context with some metadata + //md := metadata.Pairs("name", "v1", "k1", "v2", "k2", "v3") + md := metadata.New(m) + ctx := metadata.NewOutgoingContext(context.Background(), md) + rpc, err := parse.InvokeRpc(ctx, stub, mds, data) if err != nil { panic(err) } @@ -82,7 +89,7 @@ func TestServer(t *testing.T) { var opts []grpc.ServerOption grpcServer := grpc.NewServer(opts...) v1.RegisterOrderServiceServer(grpcServer, &Order{}) - reflection.Register(grpcServer) + //reflection.Register(grpcServer) fmt.Println("gRPC server started at ", port) if err := grpcServer.Serve(lis); err != nil { @@ -95,6 +102,11 @@ type Order struct { } func (o *Order) Create(ctx context.Context, in *v1.OrderApiCreate) (*v1.Order, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Errorf(codes.DataLoss, "failed to get metadata") + } + fmt.Println(md) time.Sleep(200 * time.Millisecond) fmt.Println(in.OrderId)