From 398c134b009e4419dc7b73f18accd1a52ebb61f2 Mon Sep 17 00:00:00 2001 From: Vincent Raman Date: Mon, 3 Aug 2020 11:00:52 +0100 Subject: [PATCH] use native json marshaling to map stubbed output (#52) * use native json marshaling to map stubbed output When an output field had a '_' in its name, it was not mapped correctly by 'mapstructure' to the protoc generated class because, in this class, the fields are translated in CamelCase. Hower, the fields have json tag with their original name. Json marshalling is then the way to fix this issue. --- Dockerfile | 3 +- example/simple/client/main.go | 2 +- example/simple/simple.pb.go | 293 ++++++++++++++++++++++---------- example/simple/simple.proto | 1 + example/simple/stub/simple.json | 19 ++- protoc-gen-gripmock/server.tmpl | 4 +- stub/storage.go | 2 +- 7 files changed, 220 insertions(+), 104 deletions(-) diff --git a/Dockerfile b/Dockerfile index d13d9af7..9f105cf5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,6 @@ RUN mkdir /stubs RUN apk -U --no-cache add git protobuf RUN go get -u -v github.com/golang/protobuf/protoc-gen-go \ - github.com/mitchellh/mapstructure \ google.golang.org/grpc \ google.golang.org/grpc/reflection \ golang.org/x/net/context \ @@ -16,7 +15,7 @@ RUN go get -u -v github.com/golang/protobuf/protoc-gen-go \ golang.org/x/tools/imports RUN go get -u -v github.com/gobuffalo/packr/v2/... \ - github.com/gobuffalo/packr/v2/packr2 + github.com/gobuffalo/packr/v2/packr2 # cloning well-known-types RUN git clone https://github.com/google/protobuf.git /protobuf-repo diff --git a/example/simple/client/main.go b/example/simple/client/main.go index 990985d7..376083bf 100644 --- a/example/simple/client/main.go +++ b/example/simple/client/main.go @@ -32,5 +32,5 @@ func main() { if err != nil { log.Fatalf("error from grpc: %v", err) } - log.Printf("Greeting: %s", r.Message) + log.Printf("Greeting: %s (return code %d)", r.Message, r.ReturnCode) } diff --git a/example/simple/simple.pb.go b/example/simple/simple.pb.go index ccb8fa77..88827039 100644 --- a/example/simple/simple.pb.go +++ b/example/simple/simple.pb.go @@ -1,118 +1,240 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// source: simple/simple.proto +// versions: +// protoc-gen-go v1.24.0-devel +// protoc v3.12.2 +// source: simple.proto package simple -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( - context "golang.org/x/net/context" + context "context" + proto "github.com/golang/protobuf/proto" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +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) +) -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 // The request message containing the user's name. type Request struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -func (m *Request) Reset() { *m = Request{} } -func (m *Request) String() string { return proto.CompactTextString(m) } -func (*Request) ProtoMessage() {} -func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_simple_e0ad890e6f6d32ea, []int{0} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } -func (m *Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Request.Unmarshal(m, b) -} -func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Request.Marshal(b, m, deterministic) -} -func (dst *Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_Request.Merge(dst, src) + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_simple_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Request) XXX_Size() int { - return xxx_messageInfo_Request.Size(m) + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Request) XXX_DiscardUnknown() { - xxx_messageInfo_Request.DiscardUnknown(m) + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_simple_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) } -var xxx_messageInfo_Request proto.InternalMessageInfo +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_simple_proto_rawDescGZIP(), []int{0} +} -func (m *Request) GetName() string { - if m != nil { - return m.Name +func (x *Request) GetName() string { + if x != nil { + return x.Name } return "" } // The response message containing the greetings type Reply struct { - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + ReturnCode int32 `protobuf:"varint,2,opt,name=return_code,json=returnCode,proto3" json:"return_code,omitempty"` } -func (m *Reply) Reset() { *m = Reply{} } -func (m *Reply) String() string { return proto.CompactTextString(m) } -func (*Reply) ProtoMessage() {} -func (*Reply) Descriptor() ([]byte, []int) { - return fileDescriptor_simple_e0ad890e6f6d32ea, []int{1} +func (x *Reply) Reset() { + *x = Reply{} + if protoimpl.UnsafeEnabled { + mi := &file_simple_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Reply) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Reply.Unmarshal(m, b) + +func (*Reply) ProtoMessage() {} + +func (x *Reply) ProtoReflect() protoreflect.Message { + mi := &file_simple_proto_msgTypes[1] + 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) } -func (m *Reply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Reply.Marshal(b, m, deterministic) + +// Deprecated: Use Reply.ProtoReflect.Descriptor instead. +func (*Reply) Descriptor() ([]byte, []int) { + return file_simple_proto_rawDescGZIP(), []int{1} } -func (dst *Reply) XXX_Merge(src proto.Message) { - xxx_messageInfo_Reply.Merge(dst, src) + +func (x *Reply) GetMessage() string { + if x != nil { + return x.Message + } + return "" } -func (m *Reply) XXX_Size() int { - return xxx_messageInfo_Reply.Size(m) + +func (x *Reply) GetReturnCode() int32 { + if x != nil { + return x.ReturnCode + } + return 0 } -func (m *Reply) XXX_DiscardUnknown() { - xxx_messageInfo_Reply.DiscardUnknown(m) + +var File_simple_proto protoreflect.FileDescriptor + +var file_simple_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0x1d, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x42, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x72, + 0x65, 0x74, 0x75, 0x72, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x32, 0x36, 0x0a, 0x08, 0x47, 0x72, 0x69, + 0x70, 0x6d, 0x6f, 0x63, 0x6b, 0x12, 0x2a, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x12, 0x0f, 0x2e, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } -var xxx_messageInfo_Reply proto.InternalMessageInfo +var ( + file_simple_proto_rawDescOnce sync.Once + file_simple_proto_rawDescData = file_simple_proto_rawDesc +) -func (m *Reply) GetMessage() string { - if m != nil { - return m.Message - } - return "" +func file_simple_proto_rawDescGZIP() []byte { + file_simple_proto_rawDescOnce.Do(func() { + file_simple_proto_rawDescData = protoimpl.X.CompressGZIP(file_simple_proto_rawDescData) + }) + return file_simple_proto_rawDescData } -func init() { - proto.RegisterType((*Request)(nil), "simple.Request") - proto.RegisterType((*Reply)(nil), "simple.Reply") +var file_simple_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_simple_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: simple.Request + (*Reply)(nil), // 1: simple.Reply +} +var file_simple_proto_depIdxs = []int32{ + 0, // 0: simple.Gripmock.SayHello:input_type -> simple.Request + 1, // 1: simple.Gripmock.SayHello:output_type -> simple.Reply + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] 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_simple_proto_init() } +func file_simple_proto_init() { + if File_simple_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_simple_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_simple_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply); 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_simple_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_simple_proto_goTypes, + DependencyIndexes: file_simple_proto_depIdxs, + MessageInfos: file_simple_proto_msgTypes, + }.Build() + File_simple_proto = out.File + file_simple_proto_rawDesc = nil + file_simple_proto_goTypes = nil + file_simple_proto_depIdxs = nil } // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConn +var _ grpc.ClientConnInterface // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 +const _ = grpc.SupportPackageIsVersion6 // GripmockClient is the client API for Gripmock service. // @@ -123,10 +245,10 @@ type GripmockClient interface { } type gripmockClient struct { - cc *grpc.ClientConn + cc grpc.ClientConnInterface } -func NewGripmockClient(cc *grpc.ClientConn) GripmockClient { +func NewGripmockClient(cc grpc.ClientConnInterface) GripmockClient { return &gripmockClient{cc} } @@ -145,6 +267,14 @@ type GripmockServer interface { SayHello(context.Context, *Request) (*Reply, error) } +// UnimplementedGripmockServer can be embedded to have forward compatible implementations. +type UnimplementedGripmockServer struct { +} + +func (*UnimplementedGripmockServer) SayHello(context.Context, *Request) (*Reply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} + func RegisterGripmockServer(s *grpc.Server, srv GripmockServer) { s.RegisterService(&_Gripmock_serviceDesc, srv) } @@ -177,20 +307,5 @@ var _Gripmock_serviceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "simple/simple.proto", -} - -func init() { proto.RegisterFile("simple/simple.proto", fileDescriptor_simple_e0ad890e6f6d32ea) } - -var fileDescriptor_simple_e0ad890e6f6d32ea = []byte{ - // 138 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0xce, 0xcc, 0x2d, - 0xc8, 0x49, 0xd5, 0x87, 0x50, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x6c, 0x10, 0x9e, 0x92, - 0x2c, 0x17, 0x7b, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62, - 0x6e, 0xaa, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x98, 0xad, 0xa4, 0xc8, 0xc5, 0x1a, 0x94, - 0x5a, 0x90, 0x53, 0x29, 0x24, 0xc1, 0xc5, 0x9e, 0x9b, 0x5a, 0x5c, 0x9c, 0x98, 0x0e, 0x93, 0x87, - 0x71, 0x8d, 0xcc, 0xb8, 0x38, 0xdc, 0x8b, 0x32, 0x0b, 0x72, 0xf3, 0x93, 0xb3, 0x85, 0xb4, 0xb8, - 0x38, 0x82, 0x13, 0x2b, 0x3d, 0x52, 0x73, 0x72, 0xf2, 0x85, 0xf8, 0xf5, 0xa0, 0x16, 0x42, 0xcd, - 0x97, 0xe2, 0x45, 0x08, 0x14, 0xe4, 0x54, 0x26, 0xb1, 0x81, 0x1d, 0x62, 0x0c, 0x08, 0x00, 0x00, - 0xff, 0xff, 0x75, 0x88, 0x72, 0x2a, 0x9f, 0x00, 0x00, 0x00, + Metadata: "simple.proto", } diff --git a/example/simple/simple.proto b/example/simple/simple.proto index 158f29e2..92392178 100644 --- a/example/simple/simple.proto +++ b/example/simple/simple.proto @@ -16,4 +16,5 @@ message Request { // The response message containing the greetings message Reply { string message = 1; + int32 return_code = 2; } \ No newline at end of file diff --git a/example/simple/stub/simple.json b/example/simple/stub/simple.json index 866abb8e..6bbf0913 100644 --- a/example/simple/stub/simple.json +++ b/example/simple/stub/simple.json @@ -1,14 +1,15 @@ { - "service":"Gripmock", - "method":"SayHello", - "input":{ - "equals":{ - "name":"tokopedia" + "service": "Gripmock", + "method": "SayHello", + "input": { + "equals": { + "name": "tokopedia" } }, - "output":{ - "data":{ - "message":"Hello Tokopedia" + "output": { + "data": { + "message": "Hello Tokopedia", + "return_code": 1 } } -} \ No newline at end of file +} diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index b55335cb..c6e52dbf 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -11,7 +11,6 @@ import ( "net" "net/http" - "github.com/mitchellh/mapstructure" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" @@ -177,6 +176,7 @@ func findStub(service, method string, in, out interface{}) error { return fmt.Errorf(respRPC.Error) } - return mapstructure.Decode(respRPC.Data, out) + data, _ := json.Marshal(respRPC.Data) + return json.Unmarshal(data, out) } {{ end }} \ No newline at end of file diff --git a/stub/storage.go b/stub/storage.go index 050df60a..38785024 100644 --- a/stub/storage.go +++ b/stub/storage.go @@ -191,7 +191,7 @@ func matches(expect, actual map[string]interface{}) bool { match, err := regexp.Match(valueExpectString, []byte(actualvalue)) if err != nil { - log.Println("Error on matching regex %s with %s error:%v", valueExpectString, actualvalue, err) + log.Printf("Error on matching regex %s with %s error:%v\n", valueExpectString, actualvalue, err) } if !match {