Skip to content

Commit caf1cf5

Browse files
Fix -module option for protoc-gen-grpc-gateway (#1754)
When option module=... is used, protoc-gen-grpc-gateway removes the module prefix a first time, then uses protogen, which tries to remove the prefix a second time. Fixes #1753
1 parent 7789014 commit caf1cf5

File tree

4 files changed

+109
-80
lines changed

4 files changed

+109
-80
lines changed

protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ go_test(
3232
deps = [
3333
"//internal/descriptor:go_default_library",
3434
"//internal/httprule:go_default_library",
35+
"@org_golang_google_protobuf//compiler/protogen:go_default_library",
3536
"@org_golang_google_protobuf//proto:go_default_library",
3637
"@org_golang_google_protobuf//types/descriptorpb:go_default_library",
38+
"@org_golang_google_protobuf//types/pluginpb:go_default_library",
3739
],
3840
)

protoc-gen-grpc-gateway/internal/gengateway/generator.go

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ type generator struct {
3232
useRequestContext bool
3333
registerFuncSuffix string
3434
pathType pathType
35-
modulePath string
3635
allowPatchFeature bool
3736
standalone bool
3837
}
3938

4039
// New returns a new generator which generates grpc gateway files.
41-
func New(reg *descriptor.Registry, useRequestContext bool, registerFuncSuffix, pathTypeString, modulePathString string,
40+
func New(reg *descriptor.Registry, useRequestContext bool, registerFuncSuffix, pathTypeString string,
4241
allowPatchFeature, standalone bool) gen.Generator {
4342
var imports []descriptor.GoPackage
4443
for _, pkgpath := range []string{
@@ -87,7 +86,6 @@ func New(reg *descriptor.Registry, useRequestContext bool, registerFuncSuffix, p
8786
useRequestContext: useRequestContext,
8887
registerFuncSuffix: registerFuncSuffix,
8988
pathType: pathType,
90-
modulePath: modulePathString,
9189
allowPatchFeature: allowPatchFeature,
9290
standalone: standalone,
9391
}
@@ -112,11 +110,7 @@ func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.Response
112110
return nil, err
113111
}
114112

115-
name, err := g.getFilePath(file)
116-
if err != nil {
117-
glog.Errorf("%v: %s", err, code)
118-
return nil, err
119-
}
113+
name := g.getFilePath(file)
120114
ext := filepath.Ext(name)
121115
base := strings.TrimSuffix(name, ext)
122116
filename := fmt.Sprintf("%s.pb.gw.go", base)
@@ -131,25 +125,13 @@ func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.Response
131125
return files, nil
132126
}
133127

134-
func (g *generator) getFilePath(file *descriptor.File) (string, error) {
128+
func (g *generator) getFilePath(file *descriptor.File) string {
135129
name := file.GetName()
136-
switch {
137-
case g.modulePath != "" && g.pathType != pathTypeImport:
138-
return "", errors.New("cannot use module= with paths=")
139-
140-
case g.modulePath != "":
141-
trimPath, pkgPath := g.modulePath+"/", file.GoPkg.Path+"/"
142-
if !strings.HasPrefix(pkgPath, trimPath) {
143-
return "", fmt.Errorf("%v: file go path does not match module prefix: %v", file.GoPkg.Path, trimPath)
144-
}
145-
return filepath.Join(strings.TrimPrefix(pkgPath, trimPath), filepath.Base(name)), nil
146-
147-
case g.pathType == pathTypeImport && file.GoPkg.Path != "":
148-
return fmt.Sprintf("%s/%s", file.GoPkg.Path, filepath.Base(name)), nil
149-
150-
default:
151-
return name, nil
130+
if g.pathType == pathTypeImport && file.GoPkg.Path != "" {
131+
return fmt.Sprintf("%s/%s", file.GoPkg.Path, filepath.Base(name))
152132
}
133+
134+
return name
153135
}
154136

155137
func (g *generator) generate(file *descriptor.File) (string, error) {

protoc-gen-grpc-gateway/internal/gengateway/generator_test.go

Lines changed: 98 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package gengateway
22

33
import (
4-
"errors"
4+
"fmt"
55
"path/filepath"
66
"strings"
77
"testing"
88

99
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
10+
"google.golang.org/protobuf/compiler/protogen"
1011
"google.golang.org/protobuf/proto"
1112
"google.golang.org/protobuf/types/descriptorpb"
13+
"google.golang.org/protobuf/types/pluginpb"
1214
)
1315

1416
func newExampleFileDescriptor() *descriptor.File {
@@ -101,11 +103,15 @@ func TestGenerateServiceWithoutBindings(t *testing.T) {
101103

102104
func TestGenerateOutputPath(t *testing.T) {
103105
cases := []struct {
104-
file *descriptor.File
105-
pathType pathType
106-
modulePath string
107-
expected string
108-
expectedError error
106+
file *descriptor.File
107+
pathType pathType
108+
modulePath string
109+
110+
// the path that function Generate should output
111+
expectedPath string
112+
// the path after protogen has remove the module prefix
113+
expectedFinalPath string
114+
expectedError bool
109115
}{
110116
{
111117
file: newExampleFileDescriptorWithGoPkg(
@@ -114,7 +120,8 @@ func TestGenerateOutputPath(t *testing.T) {
114120
Name: "example_pb",
115121
},
116122
),
117-
expected: "example.com/path/to/example",
123+
expectedPath: "example.com/path/to/example",
124+
expectedFinalPath: "example.com/path/to/example",
118125
},
119126
{
120127
file: newExampleFileDescriptorWithGoPkg(
@@ -123,7 +130,8 @@ func TestGenerateOutputPath(t *testing.T) {
123130
Name: "example_pb",
124131
},
125132
),
126-
expected: "example",
133+
expectedPath: "example",
134+
expectedFinalPath: "example",
127135
},
128136
{
129137
file: newExampleFileDescriptorWithGoPkg(
@@ -133,7 +141,9 @@ func TestGenerateOutputPath(t *testing.T) {
133141
},
134142
),
135143
pathType: pathTypeSourceRelative,
136-
expected: ".",
144+
145+
expectedPath: ".",
146+
expectedFinalPath: ".",
137147
},
138148
{
139149
file: newExampleFileDescriptorWithGoPkg(
@@ -143,7 +153,9 @@ func TestGenerateOutputPath(t *testing.T) {
143153
},
144154
),
145155
pathType: pathTypeSourceRelative,
146-
expected: ".",
156+
157+
expectedPath: ".",
158+
expectedFinalPath: ".",
147159
},
148160
{
149161
file: newExampleFileDescriptorWithGoPkg(
@@ -153,7 +165,9 @@ func TestGenerateOutputPath(t *testing.T) {
153165
},
154166
),
155167
modulePath: "example.com/path/root",
156-
expected: ".",
168+
169+
expectedPath: "example.com/path/root",
170+
expectedFinalPath: ".",
157171
},
158172
{
159173
file: newExampleFileDescriptorWithGoPkg(
@@ -163,7 +177,9 @@ func TestGenerateOutputPath(t *testing.T) {
163177
},
164178
),
165179
modulePath: "example.com/path/to",
166-
expected: "example",
180+
181+
expectedPath: "example.com/path/to/example",
182+
expectedFinalPath: "example",
167183
},
168184
{
169185
file: newExampleFileDescriptorWithGoPkg(
@@ -173,7 +189,9 @@ func TestGenerateOutputPath(t *testing.T) {
173189
},
174190
),
175191
modulePath: "example.com/path/to",
176-
expected: "example/with/many/nested/paths",
192+
193+
expectedPath: "example.com/path/to/example/with/many/nested/paths",
194+
expectedFinalPath: "example/with/many/nested/paths",
177195
},
178196

179197
// Error cases
@@ -186,7 +204,7 @@ func TestGenerateOutputPath(t *testing.T) {
186204
),
187205
modulePath: "example.com/path/root",
188206
pathType: pathTypeSourceRelative, // Not allowed
189-
expectedError: errors.New("cannot use module= with paths="),
207+
expectedError: true,
190208
},
191209
{
192210
file: newExampleFileDescriptorWithGoPkg(
@@ -195,50 +213,77 @@ func TestGenerateOutputPath(t *testing.T) {
195213
Name: "example_pb",
196214
},
197215
),
198-
modulePath: "example.com/path/root",
199-
expectedError: errors.New("example.com/path/rootextra: file go path does not match module prefix: example.com/path/root/"),
216+
modulePath: "example.com/path/root", // Not a prefix of path
217+
expectedError: true,
200218
},
201219
}
202220

203-
for _, c := range cases {
204-
g := &generator{
205-
pathType: c.pathType,
206-
modulePath: c.modulePath,
207-
}
221+
for i, c := range cases {
222+
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
223+
g := &generator{
224+
pathType: c.pathType,
225+
}
226+
227+
file := c.file
228+
gots, err := g.Generate([]*descriptor.File{crossLinkFixture(file)})
229+
230+
// We don't expect an error during generation
231+
if err != nil {
232+
t.Errorf("Generate(%#v) failed with %v; wants success", file, err)
233+
return
234+
}
235+
236+
if len(gots) != 1 {
237+
t.Errorf("Generate(%#v) failed; expects one result, got %d", file, len(gots))
238+
return
239+
}
208240

209-
file := c.file
210-
gots, err := g.Generate([]*descriptor.File{crossLinkFixture(file)})
241+
got := gots[0]
242+
if got.Name == nil {
243+
t.Errorf("Generate(%#v) failed; expects non-nil Name(%v)", file, got.Name)
244+
return
245+
}
246+
247+
gotPath := filepath.Dir(*got.Name)
248+
if gotPath != c.expectedPath && !c.expectedError {
249+
t.Errorf("Generate(%#v) failed; got path: %s expected path: %s", file, gotPath, c.expectedPath)
250+
return
251+
}
252+
253+
// We now use codegen to verify how it optionally removes the module prefix
254+
255+
reqParam := ""
256+
if c.modulePath != "" {
257+
reqParam = "module=" + c.modulePath
258+
}
259+
req := &pluginpb.CodeGeneratorRequest{Parameter: &reqParam}
260+
plugin, err := protogen.Options{}.New(req)
261+
if err != nil {
262+
t.Errorf("Unexpected error during plugin creation: %v", err)
263+
}
264+
265+
genFile := plugin.NewGeneratedFile(got.GetName(), protogen.GoImportPath(got.GoPkg.Path))
266+
_, _ = genFile.Write([]byte(got.GetContent()))
267+
resp := plugin.Response()
268+
269+
if !c.expectedError && resp.GetError() != "" {
270+
t.Errorf("Unexpected error in protogen response: %v", resp.GetError())
271+
return
272+
}
273+
274+
if c.expectedError {
275+
if resp.GetError() == "" {
276+
t.Error("Expected an non-null error in protogen response")
277+
}
278+
return
279+
}
211280

212-
// If we expect an error response, check it matches what we want
213-
if c.expectedError != nil {
214-
if err == nil || err.Error() != c.expectedError.Error() {
215-
t.Errorf("Generate(%#v) failed with %v; wants error of: %v", file, err, c.expectedError)
281+
finalName := resp.File[0].GetName()
282+
gotPath = filepath.Dir(finalName)
283+
if gotPath != c.expectedFinalPath {
284+
t.Errorf("After protogen, got path: %s expected path: %s", gotPath, c.expectedFinalPath)
285+
return
216286
}
217-
return
218-
}
219-
220-
// Handle case where we don't expect an error
221-
if err != nil {
222-
t.Errorf("Generate(%#v) failed with %v; wants success", file, err)
223-
return
224-
}
225-
226-
if len(gots) != 1 {
227-
t.Errorf("Generate(%#v) failed; expects on result got %d", file, len(gots))
228-
return
229-
}
230-
231-
got := gots[0]
232-
if got.Name == nil {
233-
t.Errorf("Generate(%#v) failed; expects non-nil Name(%v)", file, got.Name)
234-
return
235-
}
236-
237-
gotPath := filepath.Dir(*got.Name)
238-
expectedPath := c.expected
239-
if gotPath != expectedPath {
240-
t.Errorf("Generate(%#v) failed; got path: %s expected path: %s", file, gotPath, expectedPath)
241-
return
242-
}
287+
})
243288
}
244289
}

protoc-gen-grpc-gateway/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var (
2828
allowDeleteBody = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
2929
grpcAPIConfiguration = flag.String("grpc_api_configuration", "", "path to gRPC API Configuration in YAML format")
3030
pathType = flag.String("paths", "", "specifies how the paths of generated files are structured")
31-
modulePath = flag.String("module", "", "specifies a module prefix that will be stripped from the go package to determine the output directory")
31+
_ = flag.String("module", "", "specifies a module prefix that will be stripped from the go package to determine the output directory")
3232
allowRepeatedFieldsInBody = flag.Bool("allow_repeated_fields_in_body", false, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option")
3333
repeatedPathParamSeparator = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`.")
3434
allowPatchFeature = flag.Bool("allow_patch_feature", true, "determines whether to use PATCH feature involving update masks (using google.protobuf.FieldMask).")
@@ -90,7 +90,7 @@ func main() {
9090
targets = append(targets, f)
9191
}
9292

93-
g := gengateway.New(reg, *useRequestContext, *registerFuncSuffix, *pathType, *modulePath, *allowPatchFeature, *standalone)
93+
g := gengateway.New(reg, *useRequestContext, *registerFuncSuffix, *pathType, *allowPatchFeature, *standalone)
9494
files, err := g.Generate(targets)
9595
for _, f := range files {
9696
glog.V(1).Infof("NewGeneratedFile %q in %s", f.GetName(), f.GoPkg)

0 commit comments

Comments
 (0)