Skip to content

Commit 71ff189

Browse files
authoredJul 18, 2022
fix(gengapic): regapic GetOperation path fallback logic (#1072)
1 parent 905be8f commit 71ff189

File tree

5 files changed

+91
-10
lines changed

5 files changed

+91
-10
lines changed
 

‎internal/gengapic/genrest.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptor.MethodDescriptorP
937937
p(" return nil, e")
938938
p("}")
939939
p("")
940-
override := g.getOperationPathOverride()
940+
override := g.getOperationPathOverride(g.descInfo.ParentFile[m].GetPackage())
941941
p("override := fmt.Sprintf(%q, resp.GetName())", override)
942942
p("return &%s{", opWrapperType)
943943
p(" lro: longrunning.InternalNewOperation(*c.LROClient, resp),")

‎internal/gengapic/helpers.go

+11
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ func hasMethod(service *descriptor.ServiceDescriptorProto, method string) bool {
122122
return false
123123
}
124124

125+
// getMethod returns the MethodDescriptorProto for the given service RPC and simple method name.
126+
func getMethod(service *descriptor.ServiceDescriptorProto, method string) *descriptor.MethodDescriptorProto {
127+
for _, m := range service.GetMethod() {
128+
if m.GetName() == method {
129+
return m
130+
}
131+
}
132+
133+
return nil
134+
}
135+
125136
// containsTransport determines if a set of transports contains a specific
126137
// transport.
127138
func containsTransport(t []transport, tr transport) bool {

‎internal/gengapic/lro.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ func (g *generator) lroCall(servName string, m *descriptor.MethodDescriptorProto
7575
}
7676

7777
func (g *generator) lroType(servName string, serv *descriptor.ServiceDescriptorProto, m *descriptor.MethodDescriptorProto) error {
78-
mFQN := fmt.Sprintf("%s.%s.%s", g.descInfo.ParentFile[serv].GetPackage(), serv.GetName(), m.GetName())
78+
protoPkg := g.descInfo.ParentFile[serv].GetPackage()
79+
mFQN := fmt.Sprintf("%s.%s.%s", protoPkg, serv.GetName(), m.GetName())
7980
lroType := lroTypeName(m.GetName())
8081
p := g.printf
8182
hasREST := containsTransport(g.opts.transports, rest)
@@ -94,7 +95,7 @@ func (g *generator) lroType(servName string, serv *descriptor.ServiceDescriptorP
9495
// TODO(ndietz) this won't work with nested message types in the same package;
9596
// migrating to protoreflect will help remove from semantic meaning in the names.
9697
if strings.IndexByte(fullName, '.') < 0 {
97-
fullName = g.descInfo.ParentFile[serv].GetPackage() + "." + fullName
98+
fullName = protoPkg + "." + fullName
9899
}
99100

100101
// When we build a map[name]Type in pbinfo, we prefix names with '.' to signify that they are fully qualified.
@@ -121,7 +122,7 @@ func (g *generator) lroType(servName string, serv *descriptor.ServiceDescriptorP
121122
// TODO(ndietz) this won't work with nested message types in the same package;
122123
// migrating to protoreflect will help remove from semantic meaning in the names.
123124
if strings.IndexByte(fullName, '.') < 0 {
124-
fullName = g.descInfo.ParentFile[serv].GetPackage() + "." + fullName
125+
fullName = protoPkg + "." + fullName
125126
}
126127
fullName = "." + fullName
127128

@@ -164,7 +165,7 @@ func (g *generator) lroType(servName string, serv *descriptor.ServiceDescriptorP
164165
p("")
165166
case rest:
166167
receiver := lowcaseRestClientName(servName)
167-
override := g.getOperationPathOverride()
168+
override := g.getOperationPathOverride(protoPkg)
168169
p("func (c *%s) %[2]s(name string) *%[2]s {", receiver, lroType)
169170
p(" override := fmt.Sprintf(%q, name)", override)
170171
p(" return &%s{", lroType)

‎internal/gengapic/mixins.go

+28-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package gengapic
1616

1717
import (
1818
"fmt"
19+
"regexp"
1920

2021
"github.com/golang/protobuf/protoc-gen-go/descriptor"
2122
"github.com/googleapis/gapic-generator-go/internal/pbinfo"
@@ -28,6 +29,17 @@ import (
2829
)
2930

3031
func init() {
32+
initMixinFiles()
33+
}
34+
35+
var apiVersionRegexp = regexp.MustCompile(`v\d+[a-z]*\d*[a-z]*\d*`)
36+
37+
var mixinFiles map[string][]*descriptor.FileDescriptorProto
38+
39+
type mixins map[string][]*descriptor.MethodDescriptorProto
40+
41+
// initMixinFiles allows test code to re-initialize the mixinFiles global.
42+
func initMixinFiles() {
3143
mixinFiles = map[string][]*descriptor.FileDescriptorProto{
3244
"google.cloud.location.Locations": {
3345
protodesc.ToFileDescriptorProto(location.File_google_cloud_location_locations_proto),
@@ -43,10 +55,6 @@ func init() {
4355
}
4456
}
4557

46-
var mixinFiles map[string][]*descriptor.FileDescriptorProto
47-
48-
type mixins map[string][]*descriptor.MethodDescriptorProto
49-
5058
// collectMixins collects the configured mixin APIs from the Service config and
5159
// gathers the appropriately configured mixin methods to generate for each.
5260
func (g *generator) collectMixins() {
@@ -246,9 +254,24 @@ func (g *generator) lookupHTTPOverride(fqn string, f func(h *annotations.HttpRul
246254
return ""
247255
}
248256

249-
func (g *generator) getOperationPathOverride() string {
257+
// getOperationPathOverride looks up the google.api.http rule for LRO GetOperation
258+
// and returns the path override. If no value is present, it synthesizes a path
259+
// using the proto package client version, for example, "/v1/{name=operations/**}".
260+
func (g *generator) getOperationPathOverride(protoPkg string) string {
250261
get := func(h *annotations.HttpRule) string { return h.GetGet() }
251262
override := g.lookupHTTPOverride("google.longrunning.Operations.GetOperation", get)
263+
if override == "" {
264+
// extract httpInfo from "hot loaded" Operations.GetOperation MethodDescriptor
265+
// Should be "/v1/{name=operations/**}"
266+
file := mixinFiles["google.longrunning.Operations"][0]
267+
mdp := getMethod(file.GetService()[0], "GetOperation")
268+
getOperationPath := getHTTPInfo(mdp).url
269+
270+
// extract client version from proto package with global regex
271+
// replace version base path in GetOperation path with proto package version segment
272+
version := apiVersionRegexp.FindStringSubmatch(protoPkg)
273+
override = apiVersionRegexp.ReplaceAllStringFunc(getOperationPath, func(s string) string { return version[0] })
274+
}
252275
override = httpPatternVarRegex.ReplaceAllStringFunc(override, func(s string) string { return "%s" })
253276
return override
254277
}

‎internal/gengapic/mixins_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,52 @@ func TestHasLROMixin(t *testing.T) {
261261
}
262262
}
263263

264+
func TestGetOperationPathOverride(t *testing.T) {
265+
for _, tc := range []struct {
266+
pkg, want string
267+
http *annotations.Http
268+
}{
269+
{
270+
pkg: "google.example.library.v1beta2",
271+
want: "/v1beta2/%s",
272+
},
273+
{
274+
pkg: "google.example.library.v1",
275+
want: "/v1/%s",
276+
},
277+
{
278+
pkg: "google.example.library.v3alpha1p1",
279+
want: "/v3alpha1p1/%s",
280+
},
281+
{
282+
pkg: "google.example.library.v2",
283+
want: "/v2/%s",
284+
http: &annotations.Http{
285+
Rules: []*annotations.HttpRule{
286+
&annotations.HttpRule{
287+
Selector: "google.longrunning.Operations.GetOperation",
288+
Pattern: &annotations.HttpRule_Get{
289+
Get: "/v2/{operation=projects/*/locations/*/operations/*}",
290+
},
291+
},
292+
},
293+
},
294+
},
295+
} {
296+
initMixinFiles()
297+
g := generator{
298+
comments: make(map[protoiface.MessageV1]string),
299+
mixins: make(mixins),
300+
serviceConfig: &serviceconfig.Service{
301+
Http: tc.http,
302+
},
303+
}
304+
if got := g.getOperationPathOverride(tc.pkg); !cmp.Equal(got, tc.want) {
305+
t.Errorf("TestGetOperationPathOverrideMissing wanted %v but got %v", tc.want, got)
306+
}
307+
}
308+
}
309+
264310
// locationMethods is just used for testing.
265311
func locationMethods() []*descriptor.MethodDescriptorProto {
266312
return mixinFiles["google.cloud.location.Locations"][0].GetService()[0].GetMethod()

0 commit comments

Comments
 (0)
Please sign in to comment.