diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index e4d4c8de..a2f7890b 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -34,4 +34,7 @@ jobs: uses: ./ with: entrypoint: example/multi-files/entrypoint.sh - + - name: Run one-of Example + uses: ./ + with: + entrypoint: example/one-of/entrypoint.sh diff --git a/.gitignore b/.gitignore index 9028d268..7c7eb365 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -vendor -Gopkg.lock +go.sum +pkged.go gripmock diff --git a/Dockerfile b/Dockerfile index 9f105cf5..b46bf010 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,7 @@ RUN go get -u -v github.com/golang/protobuf/protoc-gen-go \ github.com/lithammer/fuzzysearch/fuzzy \ golang.org/x/tools/imports -RUN go get -u -v github.com/gobuffalo/packr/v2/... \ - github.com/gobuffalo/packr/v2/packr2 +RUN go get github.com/markbates/pkger/cmd/pkger # cloning well-known-types RUN git clone https://github.com/google/protobuf.git /protobuf-repo @@ -33,13 +32,11 @@ COPY . /go/src/github.com/tokopedia/gripmock WORKDIR /go/src/github.com/tokopedia/gripmock/protoc-gen-gripmock -RUN packr2 +RUN pkger # install generator plugin RUN go install -v -RUN packr2 clean - WORKDIR /go/src/github.com/tokopedia/gripmock # install gripmock diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index 93e31bc3..00000000 --- a/Gopkg.toml +++ /dev/null @@ -1,58 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - branch = "master" - name = "github.com/alecthomas/participle" - -[[constraint]] - name = "github.com/go-chi/chi" - version = "3.3.2" - -[[constraint]] - name = "github.com/golang/protobuf" - version = "1.0.0" - -[[constraint]] - name = "github.com/lithammer/fuzzysearch" - version = "1.0.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "1.2.1" - -[[constraint]] - branch = "master" - name = "golang.org/x/net" - -[[constraint]] - name = "google.golang.org/grpc" - version = "1.11.3" - -[prune] - go-tests = true - unused-packages = true diff --git a/example/one-of/client/main.go b/example/one-of/client/main.go new file mode 100644 index 00000000..9cb3e6cb --- /dev/null +++ b/example/one-of/client/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + oneof "github.com/tokopedia/gripmock/example/one-of" + + "google.golang.org/grpc" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + // Set up a connection to the server. + conn, err := grpc.DialContext(ctx, "localhost:4770", grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + + c := oneof.NewGripmockClient(conn) + + // Contact the server and print out its response. + name := "tokopedia" + if len(os.Args) > 1 { + name = os.Args[1] + } + r, err := c.SayHello(context.Background(), &oneof.Request{Name: name}) + if err != nil { + log.Fatalf("error from grpc: %v", err) + } + log.Printf("Reply1: %s", r.GetReply1()) + log.Printf("Reply2: %s", r.GetReply2()) + log.Printf("ReplyType: %s", r.GetReplyType()) +} diff --git a/example/one-of/entrypoint.sh b/example/one-of/entrypoint.sh new file mode 100755 index 00000000..71418bf3 --- /dev/null +++ b/example/one-of/entrypoint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +# this file is used by .github/workflows/integration-test.yml + +gripmock --stub=example/one-of/stub example/one-of/oneof.proto & + +go run example/one-of/client/*.go \ No newline at end of file diff --git a/example/one-of/oneof.pb.go b/example/one-of/oneof.pb.go new file mode 100644 index 00000000..c086c330 --- /dev/null +++ b/example/one-of/oneof.pb.go @@ -0,0 +1,479 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.13.0 +// source: oneof.proto + +package oneof + +import ( + 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" +) + +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 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 { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_oneof_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_oneof_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 Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_oneof_proto_rawDescGZIP(), []int{0} +} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type Reply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to ReplyType: + // *Reply_Reply1 + // *Reply_Reply2 + ReplyType isReply_ReplyType `protobuf_oneof:"replyType"` +} + +func (x *Reply) Reset() { + *x = Reply{} + if protoimpl.UnsafeEnabled { + mi := &file_oneof_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply) ProtoMessage() {} + +func (x *Reply) ProtoReflect() protoreflect.Message { + mi := &file_oneof_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) +} + +// Deprecated: Use Reply.ProtoReflect.Descriptor instead. +func (*Reply) Descriptor() ([]byte, []int) { + return file_oneof_proto_rawDescGZIP(), []int{1} +} + +func (m *Reply) GetReplyType() isReply_ReplyType { + if m != nil { + return m.ReplyType + } + return nil +} + +func (x *Reply) GetReply1() *Reply1 { + if x, ok := x.GetReplyType().(*Reply_Reply1); ok { + return x.Reply1 + } + return nil +} + +func (x *Reply) GetReply2() *Reply2 { + if x, ok := x.GetReplyType().(*Reply_Reply2); ok { + return x.Reply2 + } + return nil +} + +type isReply_ReplyType interface { + isReply_ReplyType() +} + +type Reply_Reply1 struct { + Reply1 *Reply1 `protobuf:"bytes,1,opt,name=reply1,proto3,oneof"` +} + +type Reply_Reply2 struct { + Reply2 *Reply2 `protobuf:"bytes,2,opt,name=reply2,proto3,oneof"` +} + +func (*Reply_Reply1) isReply_ReplyType() {} + +func (*Reply_Reply2) isReply_ReplyType() {} + +// usual response type +type Reply1 struct { + 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 (x *Reply1) Reset() { + *x = Reply1{} + if protoimpl.UnsafeEnabled { + mi := &file_oneof_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply1) ProtoMessage() {} + +func (x *Reply1) ProtoReflect() protoreflect.Message { + mi := &file_oneof_proto_msgTypes[2] + 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 Reply1.ProtoReflect.Descriptor instead. +func (*Reply1) Descriptor() ([]byte, []int) { + return file_oneof_proto_rawDescGZIP(), []int{2} +} + +func (x *Reply1) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Reply1) GetReturnCode() int32 { + if x != nil { + return x.ReturnCode + } + return 0 +} + +// other response type +type Reply2 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Other int32 `protobuf:"varint,1,opt,name=other,proto3" json:"other,omitempty"` +} + +func (x *Reply2) Reset() { + *x = Reply2{} + if protoimpl.UnsafeEnabled { + mi := &file_oneof_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reply2) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reply2) ProtoMessage() {} + +func (x *Reply2) ProtoReflect() protoreflect.Message { + mi := &file_oneof_proto_msgTypes[3] + 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 Reply2.ProtoReflect.Descriptor instead. +func (*Reply2) Descriptor() ([]byte, []int) { + return file_oneof_proto_rawDescGZIP(), []int{3} +} + +func (x *Reply2) GetOther() int32 { + if x != nil { + return x.Other + } + return 0 +} + +var File_oneof_proto protoreflect.FileDescriptor + +var file_oneof_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x6f, + 0x6e, 0x65, 0x6f, 0x66, 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, 0x66, 0x0a, 0x05, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x27, 0x0a, 0x06, + 0x72, 0x65, 0x70, 0x6c, 0x79, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6f, + 0x6e, 0x65, 0x6f, 0x66, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x31, 0x48, 0x00, 0x52, 0x06, 0x72, + 0x65, 0x70, 0x6c, 0x79, 0x31, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x2e, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x32, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x42, 0x0b, + 0x0a, 0x09, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x54, 0x79, 0x70, 0x65, 0x22, 0x43, 0x0a, 0x06, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x31, 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, + 0x22, 0x1e, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x74, + 0x68, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x32, 0x34, 0x0a, 0x08, 0x47, 0x72, 0x69, 0x70, 0x6d, 0x6f, 0x63, 0x6b, 0x12, 0x28, 0x0a, 0x08, + 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x0e, 0x2e, 0x6f, 0x6e, 0x65, 0x6f, 0x66, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x6f, 0x6e, 0x65, 0x6f, 0x66, + 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_oneof_proto_rawDescOnce sync.Once + file_oneof_proto_rawDescData = file_oneof_proto_rawDesc +) + +func file_oneof_proto_rawDescGZIP() []byte { + file_oneof_proto_rawDescOnce.Do(func() { + file_oneof_proto_rawDescData = protoimpl.X.CompressGZIP(file_oneof_proto_rawDescData) + }) + return file_oneof_proto_rawDescData +} + +var file_oneof_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_oneof_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: oneof.Request + (*Reply)(nil), // 1: oneof.Reply + (*Reply1)(nil), // 2: oneof.Reply1 + (*Reply2)(nil), // 3: oneof.Reply2 +} +var file_oneof_proto_depIdxs = []int32{ + 2, // 0: oneof.Reply.reply1:type_name -> oneof.Reply1 + 3, // 1: oneof.Reply.reply2:type_name -> oneof.Reply2 + 0, // 2: oneof.Gripmock.SayHello:input_type -> oneof.Request + 1, // 3: oneof.Gripmock.SayHello:output_type -> oneof.Reply + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_oneof_proto_init() } +func file_oneof_proto_init() { + if File_oneof_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_oneof_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_oneof_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 + } + } + file_oneof_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply1); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_oneof_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reply2); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_oneof_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*Reply_Reply1)(nil), + (*Reply_Reply2)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_oneof_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_oneof_proto_goTypes, + DependencyIndexes: file_oneof_proto_depIdxs, + MessageInfos: file_oneof_proto_msgTypes, + }.Build() + File_oneof_proto = out.File + file_oneof_proto_rawDesc = nil + file_oneof_proto_goTypes = nil + file_oneof_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +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.SupportPackageIsVersion6 + +// GripmockClient is the client API for Gripmock service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GripmockClient interface { + // simple unary method + SayHello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error) +} + +type gripmockClient struct { + cc grpc.ClientConnInterface +} + +func NewGripmockClient(cc grpc.ClientConnInterface) GripmockClient { + return &gripmockClient{cc} +} + +func (c *gripmockClient) SayHello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error) { + out := new(Reply) + err := c.cc.Invoke(ctx, "/oneof.Gripmock/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GripmockServer is the server API for Gripmock service. +type GripmockServer interface { + // simple unary method + 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) +} + +func _Gripmock_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Request) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GripmockServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/oneof.Gripmock/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GripmockServer).SayHello(ctx, req.(*Request)) + } + return interceptor(ctx, in, info, handler) +} + +var _Gripmock_serviceDesc = grpc.ServiceDesc{ + ServiceName: "oneof.Gripmock", + HandlerType: (*GripmockServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Gripmock_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "oneof.proto", +} diff --git a/example/one-of/oneof.proto b/example/one-of/oneof.proto new file mode 100644 index 00000000..4042bbae --- /dev/null +++ b/example/one-of/oneof.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package oneof; + +// The Gripmock service definition. +service Gripmock { + // simple unary method + rpc SayHello (Request) returns (Reply); +} + +// The request message containing the user's name. +message Request { + string name = 1; +} + +message Reply { + oneof replyType { + Reply1 reply1 = 1; + Reply2 reply2 = 2; + } +} + +// usual response type +message Reply1 { + string message = 1; + int32 return_code = 2; +} + +// other response type +message Reply2 { + int32 other = 1; +} \ No newline at end of file diff --git a/example/one-of/stub/oneof.json b/example/one-of/stub/oneof.json new file mode 100644 index 00000000..82e2f56f --- /dev/null +++ b/example/one-of/stub/oneof.json @@ -0,0 +1,16 @@ +{ + "service":"Gripmock", + "method":"SayHello", + "input":{ + "equals":{ + "name":"tokopedia" + } + }, + "output":{ + "data":{ + "reply1":{ + "message":"Hello GripMock" + } + } + } + } \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..973d64b4 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module github.com/tokopedia/gripmock + +go 1.15 + +require ( + github.com/go-chi/chi v4.1.2+incompatible + github.com/gobuffalo/here v0.6.2 // indirect + github.com/golang/protobuf v1.4.3 + github.com/lithammer/fuzzysearch v1.1.1 + github.com/markbates/pkger v0.17.1 + github.com/stretchr/testify v1.6.1 + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b + golang.org/x/sys v0.0.0-20201112073958-5cba982894dd // indirect + golang.org/x/text v0.3.4 // indirect + golang.org/x/tools v0.0.0-20201111224557-41a3a589386c + google.golang.org/genproto v0.0.0-20201111145450-ac7456db90a6 + google.golang.org/grpc v1.33.2 + google.golang.org/protobuf v1.25.0 + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect +) diff --git a/protoc-gen-gripmock/generator.go b/protoc-gen-gripmock/generator.go index eb9cf7c9..6f523ec6 100644 --- a/protoc-gen-gripmock/generator.go +++ b/protoc-gen-gripmock/generator.go @@ -10,52 +10,67 @@ import ( "strings" "text/template" - "github.com/gobuffalo/packr/v2" + "google.golang.org/protobuf/types/pluginpb" + "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/golang/protobuf/protoc-gen-go/generator" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" + "github.com/markbates/pkger" "golang.org/x/tools/imports" + "google.golang.org/protobuf/compiler/protogen" ) func main() { - gen := generator.New() - byt, err := ioutil.ReadAll(os.Stdin) - if err != nil { - log.Fatalf("Failed to read input: %v", err) + // Tip of the hat to Tim Coulson + // https://medium.com/@tim.r.coulson/writing-a-protoc-plugin-with-google-golang-org-protobuf-cd5aa75f5777 + + // Protoc passes pluginpb.CodeGeneratorRequest in via stdin + // marshalled with Protobuf + input, _ := ioutil.ReadAll(os.Stdin) + var request pluginpb.CodeGeneratorRequest + if err := proto.Unmarshal(input, &request); err != nil { + log.Fatalf("error unmarshalling [%s]: %v", string(input), err) } - err = proto.Unmarshal(byt, gen.Request) + // Initialise our plugin with default options + opts := protogen.Options{} + plugin, err := opts.New(&request) if err != nil { - log.Fatalf("Failed to unmarshal proto: %v", err) + log.Fatalf("error initializing plugin: %v", err) + } + + protos := make([]*descriptor.FileDescriptorProto, len(plugin.Files)) + for index, file := range plugin.Files { + protos[index] = file.Proto } - gen.CommandLineParameters(gen.Request.GetParameter()) + params := make(map[string]string) + for _, param := range strings.Split(request.GetParameter(), ",") { + split := strings.Split(param, "=") + params[split[0]] = split[1] + } buf := new(bytes.Buffer) - err = generateServer(gen.Request.ProtoFile, &Options{ + err = generateServer(protos, &Options{ writer: buf, - adminPort: gen.Param["admin-port"], - grpcAddr: fmt.Sprintf("%s:%s", gen.Param["grpc-address"], gen.Param["grpc-port"]), + adminPort: params["admin-port"], + grpcAddr: fmt.Sprintf("%s:%s", params["grpc-address"], params["grpc-port"]), }) + if err != nil { log.Fatalf("Failed to generate server %v", err) } - gen.Response.File = []*plugin_go.CodeGeneratorResponse_File{ - { - Name: proto.String("server.go"), - Content: proto.String(buf.String()), - }, - } - data, err := proto.Marshal(gen.Response) - if err != nil { - gen.Error(err, "failed to marshal output proto") - } - _, err = os.Stdout.Write(data) + file := plugin.NewGeneratedFile("server.go", ".") + file.Write(buf.Bytes()) + + // Generate a response from our plugin and marshall as protobuf + out, err := proto.Marshal(plugin.Response()) if err != nil { - gen.Error(err, "failed to write output proto") + log.Fatalf("error marshalling plugin response: %v", err) } + + // Write the response to stdout, to be picked up by protoc + os.Stdout.Write(out) } type generatorParam struct { @@ -99,13 +114,17 @@ type Options struct { var SERVER_TEMPLATE string func init() { - tmplBox := packr.New("template", "") + f, err := pkger.Open("/protoc-gen-gripmock/server.tmpl") + if err != nil { + log.Fatalf("error opening server.tmpl: %s", err) + } - s, err := tmplBox.FindString("server.tmpl") + bytes, err := ioutil.ReadAll(f) if err != nil { - log.Fatal("Can't find server.tmpl") + log.Fatalf("error reading server.tmpl: %s", err) } - SERVER_TEMPLATE = s + + SERVER_TEMPLATE = string(bytes) } func generateServer(protos []*descriptor.FileDescriptorProto, opt *Options) error { @@ -199,6 +218,12 @@ func getGoPackage(proto *descriptor.FileDescriptorProto) (alias string, goPackag split := strings.Split(splitSlash[len(splitSlash)-1], ".") alias = split[0] } + + // Aliases can't be keywords + if isKeyword(alias) { + alias = fmt.Sprintf("%s_pb", alias) + } + return } @@ -258,3 +283,41 @@ func getMessageType(protos []*descriptor.FileDescriptorProto, deps []string, tip } return targetType } + +func isKeyword(word string) bool { + keywords := [...]string{ + "break", + "case", + "chan", + "const", + "continue", + "default", + "defer", + "else", + "fallthrough", + "for", + "func", + "go", + "goto", + "if", + "import", + "interface", + "map", + "package", + "range", + "return", + "select", + "struct", + "switch", + "type", + "var", + } + + for _, keyword := range keywords { + if strings.ToLower(word) == keyword { + return true + } + } + + return false +} diff --git a/protoc-gen-gripmock/server.tmpl b/protoc-gen-gripmock/server.tmpl index c6e52dbf..4cd75e68 100644 --- a/protoc-gen-gripmock/server.tmpl +++ b/protoc-gen-gripmock/server.tmpl @@ -11,9 +11,11 @@ import ( "net" "net/http" + "github.com/golang/protobuf/jsonpb" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" + "google.golang.org/protobuf/runtime/protoiface" ) {{ range $package, $alias := .Dependencies }} import {{$alias}} "{{$package}}" @@ -144,7 +146,7 @@ type response struct { Error string `json:"error"` } -func findStub(service, method string, in, out interface{}) error { +func findStub(service, method string, in, out protoiface.MessageV1) error { url := fmt.Sprintf("http://localhost%s/find", HTTP_PORT) pyl := payload{ Service: service, @@ -177,6 +179,6 @@ func findStub(service, method string, in, out interface{}) error { } data, _ := json.Marshal(respRPC.Data) - return json.Unmarshal(data, out) + return jsonpb.Unmarshal(bytes.NewReader(data), out) } {{ end }} \ No newline at end of file