Skip to content

Commit 98f7a13

Browse files
authored
feat(gengapic): add snippets (googleapis#1220)
* add option omit-snippets (default: false) * add snippet support to gengapic.go and example.go * copy snippets/metadata and helpers from google-cloud-go * continue to skip unimplemented streaming examples and snippets * improve unit test coverage
1 parent 2baef11 commit 98f7a13

31 files changed

+3173
-43
lines changed

.github/snippet-bot.yml

+5
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
aggregateChecks: false
22
alwaysCreateStatusCheck: false
3+
4+
# The generator prints region tags and snippet-bot wants to correct
5+
# the printing code.
6+
ignoreFiles:
7+
- "internal/**"

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ The configuration supported by the plugin option includes:
101101
* Not enabled by default.
102102
* Only effective when `rest` is included as a `transport` to be generated.
103103

104+
* `omit-snippets`: disable generation of code snippets to the `internal/generated/snippets` path. The default is `false`.
105+
104106
Bazel
105107
-----
106108

@@ -165,6 +167,8 @@ following attributes:
165167
* Default is `False`.
166168
* Only effective when `rest` is included as a `transport` to be generated.
167169

170+
* `omit_snippets`: if `True`, code snippets will be generated to the `internal/generated/snippets` path. The default is `false`.
171+
168172
Docker Wrapper
169173
--------------
170174
The generator can also be executed via a Docker container. The image containes `protoc`, the microgenerator

internal/gengapic/BUILD.bazel

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ go_library(
1919
"mixins.go",
2020
"options.go",
2121
"paging.go",
22+
"snippets.go",
2223
"stream.go",
2324
"test_utils.go",
2425
],
@@ -30,6 +31,7 @@ go_library(
3031
"//internal/license",
3132
"//internal/pbinfo",
3233
"//internal/printer",
34+
"//internal/snippets",
3335
"@com_github_ghodss_yaml//:yaml",
3436
"@com_gitlab_golang_commonmark_markdown//:markdown",
3537
"@com_google_cloud_go_iam//apiv1/iampb",
@@ -46,6 +48,7 @@ go_library(
4648
"@org_golang_google_protobuf//proto",
4749
"@org_golang_google_protobuf//reflect/protodesc",
4850
"@org_golang_google_protobuf//runtime/protoiface",
51+
"@org_golang_google_protobuf//types/descriptorpb",
4952
],
5053
)
5154

@@ -70,6 +73,7 @@ go_test(
7073
deps = [
7174
"//internal/grpc_service_config",
7275
"//internal/pbinfo",
76+
"//internal/snippets",
7377
"//internal/txtdiff",
7478
"@com_github_google_go_cmp//cmp",
7579
"@com_google_cloud_go_longrunning//autogen/longrunningpb",
@@ -78,6 +82,7 @@ go_test(
7882
"@go_googleapis//google/api:httpbody_go_proto",
7983
"@go_googleapis//google/api:serviceconfig_go_proto",
8084
"@go_googleapis//google/cloud:extended_operations_go_proto",
85+
"@go_googleapis//google/longrunning:longrunning_go_proto",
8186
"@go_googleapis//google/rpc:code_go_proto",
8287
"@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
8388
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",

internal/gengapic/client_init.go

+38-13
Original file line numberDiff line numberDiff line change
@@ -242,21 +242,25 @@ func (g *generator) genClientWrapperMethod(m *descriptor.MethodDescriptorProto,
242242
}
243243

244244
// Generate method documentation just before any method is generated.
245-
g.methodDoc(m)
245+
g.methodDoc(m, serv)
246246

247247
if m.GetOutputType() == emptyType {
248-
p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) error {",
249-
clientTypeName, m.GetName(), inSpec.Name, inType.GetName())
248+
reqTyp := fmt.Sprintf("%s.%s", inSpec.Name, inType.GetName())
249+
p("func (c *%s) %s(ctx context.Context, req *%s, opts ...gax.CallOption) error {",
250+
clientTypeName, m.GetName(), reqTyp)
250251
p(" return c.internalClient.%s(ctx, req, opts...)", m.GetName())
251252
p("}")
252253
p("")
254+
255+
g.addSnippetsMetadataParams(m, serv.GetName(), reqTyp)
253256
return nil
254257
}
255258

256259
if g.isLRO(m) {
260+
reqTyp := fmt.Sprintf("%s.%s", inSpec.Name, inType.GetName())
257261
lroType := lroTypeName(m.GetName())
258-
p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) (*%s, error) {",
259-
clientTypeName, m.GetName(), inSpec.Name, inType.GetName(), lroType)
262+
p("func (c *%s) %s(ctx context.Context, req *%s, opts ...gax.CallOption) (*%s, error) {",
263+
clientTypeName, m.GetName(), reqTyp, lroType)
260264
p(" return c.internalClient.%s(ctx, req, opts...)", m.GetName())
261265
p("}")
262266
p("")
@@ -266,21 +270,28 @@ func (g *generator) genClientWrapperMethod(m *descriptor.MethodDescriptorProto,
266270
p(" return c.internalClient.%s(name)", lroType)
267271
p("}")
268272
p("")
273+
274+
g.addSnippetsMetadataParams(m, serv.GetName(), reqTyp)
275+
g.addSnippetsMetadataResult(m, serv.GetName(), lroType)
269276
return nil
270277
}
271278

272279
if pf, _, err := g.getPagingFields(m); err != nil {
273280
return err
274281
} else if pf != nil {
282+
reqTyp := fmt.Sprintf("%s.%s", inSpec.Name, inType.GetName())
275283
iter, err := g.iterTypeOf(pf)
276284
if err != nil {
277285
return err
278286
}
279-
p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) *%s {",
280-
clientTypeName, m.GetName(), inSpec.Name, inType.GetName(), iter.iterTypeName)
287+
p("func (c *%s) %s(ctx context.Context, req *%s, opts ...gax.CallOption) *%s {",
288+
clientTypeName, m.GetName(), reqTyp, iter.iterTypeName)
281289
p(" return c.internalClient.%s(ctx, req, opts...)", m.GetName())
282290
p("}")
283291
p("")
292+
293+
g.addSnippetsMetadataParams(m, serv.GetName(), reqTyp)
294+
g.addSnippetsMetadataResult(m, serv.GetName(), iter.iterTypeName)
284295
return nil
285296
}
286297

@@ -291,34 +302,48 @@ func (g *generator) genClientWrapperMethod(m *descriptor.MethodDescriptorProto,
291302
return err
292303
}
293304

294-
p("func (c *%s) %s(ctx context.Context, opts ...gax.CallOption) (%s.%s_%sClient, error) {",
295-
clientTypeName, m.GetName(), servSpec.Name, serv.GetName(), m.GetName())
305+
retTyp := fmt.Sprintf("%s.%s_%sClient", servSpec.Name, serv.GetName(), m.GetName())
306+
p("func (c *%s) %s(ctx context.Context, opts ...gax.CallOption) (%s, error) {",
307+
clientTypeName, m.GetName(), retTyp)
296308
p(" return c.internalClient.%s(ctx, opts...)", m.GetName())
297309
p("}")
298310
p("")
311+
312+
g.addSnippetsMetadataParams(m, serv.GetName(), "")
313+
g.addSnippetsMetadataResult(m, serv.GetName(), retTyp)
299314
return nil
300315
case m.GetServerStreaming():
301316
servSpec, err := g.descInfo.ImportSpec(serv)
302317
if err != nil {
303318
return err
304319
}
305-
p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) (%s.%s_%sClient, error) {",
306-
clientTypeName, m.GetName(), inSpec.Name, inType.GetName(), servSpec.Name, serv.GetName(), m.GetName())
320+
321+
reqTyp := fmt.Sprintf("%s.%s", inSpec.Name, inType.GetName())
322+
retTyp := fmt.Sprintf("%s.%s_%sClient", servSpec.Name, serv.GetName(), m.GetName())
323+
p("func (c *%s) %s(ctx context.Context, req *%s, opts ...gax.CallOption) (%s, error) {",
324+
clientTypeName, m.GetName(), reqTyp, retTyp)
307325
p(" return c.internalClient.%s(ctx, req, opts...)", m.GetName())
308326
p("}")
309327
p("")
328+
329+
g.addSnippetsMetadataParams(m, serv.GetName(), reqTyp)
330+
g.addSnippetsMetadataResult(m, serv.GetName(), retTyp)
310331
return nil
311332
default:
333+
reqTyp := fmt.Sprintf("%s.%s", inSpec.Name, inType.GetName())
312334
retTyp, err := g.returnType(m)
313335
if err != nil {
314336
return err
315337
}
316338

317-
p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) (%s, error) {",
318-
clientTypeName, m.GetName(), inSpec.Name, inType.GetName(), retTyp)
339+
p("func (c *%s) %s(ctx context.Context, req *%s, opts ...gax.CallOption) (%s, error) {",
340+
clientTypeName, m.GetName(), reqTyp, retTyp)
319341
p(" return c.internalClient.%s(ctx, req, opts...)", m.GetName())
320342
p("}")
321343
p("")
344+
345+
g.addSnippetsMetadataParams(m, serv.GetName(), reqTyp)
346+
g.addSnippetsMetadataResult(m, serv.GetName(), retTyp)
322347
return nil
323348
}
324349

internal/gengapic/client_init_test.go

+35
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/google/go-cmp/cmp"
2525
conf "github.com/googleapis/gapic-generator-go/internal/grpc_service_config"
2626
"github.com/googleapis/gapic-generator-go/internal/pbinfo"
27+
"github.com/googleapis/gapic-generator-go/internal/snippets"
2728
"github.com/googleapis/gapic-generator-go/internal/txtdiff"
2829
"google.golang.org/genproto/googleapis/api/annotations"
2930
"google.golang.org/genproto/googleapis/api/serviceconfig"
@@ -385,6 +386,7 @@ func TestClientInit(t *testing.T) {
385386
customOpServ *descriptor.ServiceDescriptorProto
386387
parameter *string
387388
imports map[pbinfo.ImportSpec]bool
389+
wantNumSnps int
388390
}{
389391
{
390392
tstName: "foo_client_init",
@@ -405,6 +407,7 @@ func TestClientInit(t *testing.T) {
405407
{Name: "locationpb", Path: "google.golang.org/genproto/googleapis/cloud/location"}: true,
406408
{Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true,
407409
},
410+
wantNumSnps: 6,
408411
},
409412
{
410413
tstName: "foo_rest_client_init",
@@ -424,6 +427,7 @@ func TestClientInit(t *testing.T) {
424427
{Path: "net/http"}: true,
425428
{Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true,
426429
},
430+
wantNumSnps: 6,
427431
},
428432
{
429433
tstName: "empty_client_init",
@@ -441,6 +445,7 @@ func TestClientInit(t *testing.T) {
441445
{Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true,
442446
{Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true,
443447
},
448+
wantNumSnps: 1,
444449
},
445450
{
446451
tstName: "lro_client_init",
@@ -460,6 +465,7 @@ func TestClientInit(t *testing.T) {
460465
{Path: "google.golang.org/grpc"}: true,
461466
{Path: "google.golang.org/grpc/metadata"}: true,
462467
},
468+
wantNumSnps: 6,
463469
},
464470
{
465471
tstName: "deprecated_client_init",
@@ -477,6 +483,7 @@ func TestClientInit(t *testing.T) {
477483
{Path: "google.golang.org/grpc/metadata"}: true,
478484
{Path: "net/http"}: true,
479485
},
486+
wantNumSnps: 1,
480487
},
481488
{
482489
tstName: "custom_op_init",
@@ -493,6 +500,7 @@ func TestClientInit(t *testing.T) {
493500
{Path: "net/http"}: true,
494501
{Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true,
495502
},
503+
wantNumSnps: 1,
496504
},
497505
} {
498506
fds := append(mixinDescriptors(), &descriptor.FileDescriptorProto{
@@ -539,13 +547,40 @@ func TestClientInit(t *testing.T) {
539547
}
540548

541549
g.reset()
550+
sm := snippets.NewMetadata("mypackage", "github.com/googleapis/mypackage", "mypackagego")
551+
sm.AddService(tst.serv.GetName(), "mypackage.googleapis.com")
552+
for _, m := range tst.serv.GetMethod() {
553+
sm.AddMethod(tst.serv.GetName(), m.GetName(), "mypackage", tst.serv.GetName(), 50)
554+
}
555+
for _, m := range g.getMixinMethods() {
556+
sm.AddMethod(tst.serv.GetName(), m.GetName(), "mypackage", tst.serv.GetName(), 50)
557+
}
558+
g.snippetMetadata = sm
542559
g.makeClients(tst.serv, tst.servName)
543560

544561
if diff := cmp.Diff(g.imports, tst.imports); diff != "" {
545562
t.Errorf("ClientInit(%s) imports got(-),want(+):\n%s", tst.tstName, diff)
546563
}
547564

548565
txtdiff.Diff(t, tst.tstName, g.pt.String(), filepath.Join("testdata", tst.tstName+".want"))
566+
mi := g.snippetMetadata.ToMetadataIndex()
567+
if got := len(mi.Snippets); got != tst.wantNumSnps {
568+
t.Errorf("%s: got %d want len %d", t.Name(), tst.wantNumSnps, got)
569+
}
570+
for _, snp := range mi.Snippets {
571+
if got := snp.ClientMethod.Parameters[0].Name; got != "ctx" {
572+
t.Errorf("%s: got %s want ctx,", t.Name(), got)
573+
}
574+
if got := snp.ClientMethod.Parameters[1].Name; got != "req" {
575+
t.Errorf("%s: got %s want req,", t.Name(), got)
576+
}
577+
if got := snp.ClientMethod.Parameters[2].Name; got != "opts" {
578+
t.Errorf("%s: got %s want opts,", t.Name(), got)
579+
}
580+
if snp.ClientMethod.ShortName != "CancelOperation" && snp.ClientMethod.ShortName != "DeleteOperation" && snp.ClientMethod.ResultType == "" {
581+
t.Errorf("%s: got empty string, want ResultType for %s", t.Name(), snp.ClientMethod.ShortName)
582+
}
583+
}
549584
}
550585
}
551586

internal/gengapic/example.go

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func (g *generator) exampleInitClient(pkgName, servName string) {
7474
p(" // TODO: Handle error.")
7575
p("}")
7676
p("defer c.Close()")
77+
78+
g.imports[pbinfo.ImportSpec{Path: "context"}] = true
7779
}
7880

7981
func (g *generator) exampleMethod(pkgName, servName string, m *descriptor.MethodDescriptorProto) error {

internal/gengapic/example_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/golang/protobuf/protoc-gen-go/descriptor"
2323
"github.com/google/go-cmp/cmp"
2424
"github.com/googleapis/gapic-generator-go/internal/pbinfo"
25+
"github.com/googleapis/gapic-generator-go/internal/snippets"
2526
"github.com/googleapis/gapic-generator-go/internal/txtdiff"
2627
"google.golang.org/genproto/googleapis/api/annotations"
2728
"google.golang.org/genproto/googleapis/api/serviceconfig"
@@ -276,6 +277,88 @@ func TestExample(t *testing.T) {
276277
}
277278
}
278279

280+
func TestGenSnippetFile(t *testing.T) {
281+
var g generator
282+
g.imports = map[pbinfo.ImportSpec]bool{}
283+
g.serviceConfig = &serviceconfig.Service{
284+
Apis: []*apipb.Api{
285+
{Name: "google.cloud.bigquery.migration.v2.MigrationService"},
286+
},
287+
}
288+
289+
protoPkg := "google.cloud.bigquery.migration.v2"
290+
libPkg := "cloud.google.com/go/bigquery/migration/apiv2"
291+
pkgName := "bigquerymigration"
292+
g.snippetMetadata = snippets.NewMetadata(protoPkg, libPkg, pkgName)
293+
294+
inputType := &descriptor.DescriptorProto{
295+
Name: proto.String("CreateMigrationWorkflowRequest"),
296+
}
297+
outputType := &descriptor.DescriptorProto{
298+
Name: proto.String("MigrationWorkflow"),
299+
}
300+
301+
file := &descriptor.FileDescriptorProto{
302+
Options: &descriptor.FileOptions{
303+
GoPackage: proto.String("cloud.google.com/go/bigquery/migration/apiv2/migrationpb"),
304+
},
305+
Package: proto.String(protoPkg),
306+
}
307+
308+
files := []*descriptor.FileDescriptorProto{}
309+
g.descInfo = pbinfo.Of(files)
310+
for _, typ := range []*descriptor.DescriptorProto{
311+
inputType, outputType,
312+
} {
313+
g.descInfo.Type[".google.cloud.bigquery.migration.v2."+typ.GetName()] = typ
314+
g.descInfo.ParentFile[typ] = file
315+
}
316+
317+
serv := &descriptor.ServiceDescriptorProto{
318+
Name: proto.String("MigrationService"),
319+
Method: []*descriptor.MethodDescriptorProto{
320+
{
321+
Name: proto.String("CreateMigrationWorkflow"),
322+
InputType: proto.String(".google.cloud.bigquery.migration.v2.CreateMigrationWorkflowRequest"),
323+
OutputType: proto.String(".google.cloud.bigquery.migration.v2.MigrationWorkflow"),
324+
},
325+
},
326+
}
327+
328+
for _, tst := range []struct {
329+
tstName string
330+
options options
331+
imports map[pbinfo.ImportSpec]bool
332+
}{
333+
{
334+
tstName: "snippet",
335+
options: options{
336+
pkgName: "migration",
337+
transports: []transport{grpc, rest},
338+
},
339+
imports: map[pbinfo.ImportSpec]bool{
340+
{Path: "context"}: true,
341+
{Name: "migrationpb", Path: "cloud.google.com/go/bigquery/migration/apiv2/migrationpb"}: true,
342+
},
343+
},
344+
} {
345+
g.reset()
346+
g.opts = &tst.options
347+
defaultHost := "bigquerymigration.googleapis.com"
348+
g.snippetMetadata.AddService(serv.GetName(), defaultHost)
349+
err := g.genSnippetFile(serv, serv.Method[0])
350+
if err != nil {
351+
t.Fatal(err)
352+
}
353+
g.commit(filepath.Join(g.opts.outDir, "temp", "snippets", "main.go"), "main")
354+
if diff := cmp.Diff(g.imports, tst.imports); diff != "" {
355+
t.Errorf("TestExample(%s): imports got(-),want(+):\n%s", tst.tstName, diff)
356+
}
357+
got := *g.resp.File[0].Content + *g.resp.File[1].Content
358+
txtdiff.Diff(t, tst.tstName, got, filepath.Join("testdata", tst.tstName+".want"))
359+
}
360+
}
361+
279362
func commonTypes(g *generator) {
280363
empty := &descriptor.DescriptorProto{
281364
Name: proto.String("Empty"),

0 commit comments

Comments
 (0)