diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 98aee5bb0..0fdd8dd4d 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -23,9 +23,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") # http_archive( name = "com_google_googleapis", - strip_prefix = "googleapis-b4b8eda2f253cbdab358d2b280468fe5f35a4ed9", - urls = ["https://github.com/googleapis/googleapis/archive/b4b8eda2f253cbdab358d2b280468fe5f35a4ed9.zip"], - sha256 = "7696de036faaccd30c2748e9709c31e6abd6997bcf4648bd43b24d5e5e38ba7a" + strip_prefix = "googleapis-8c42b6d576d865d40398cd67ed37215748721868", + urls = ["https://github.com/googleapis/googleapis/archive/8c42b6d576d865d40398cd67ed37215748721868.zip"], + sha256 = "a17552af336b93a1de63ac67189bd586ad1b8f44abb1dcea9ee5433d8eed5feb" ) load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language") switched_rules_by_language(name = "com_google_googleapis_imports", grpc = True) diff --git a/client/doc.go b/client/doc.go index 9893d5360..d21f12eb5 100644 --- a/client/doc.go +++ b/client/doc.go @@ -110,7 +110,9 @@ func checkDisableDeadlines() (bool, error) { // DefaultAuthScopes reports the default set of authentication scopes to use with this package. func DefaultAuthScopes() []string { - return []string{} + return []string{ + "", + } } // versionGo returns the Go runtime version. The returned string diff --git a/client/echo_client.go b/client/echo_client.go index 1b1b1283d..fb0323906 100644 --- a/client/echo_client.go +++ b/client/echo_client.go @@ -164,7 +164,9 @@ type internalEchoClient interface { // side streaming, client side streaming, and bidirectional streaming. This // service also exposes methods that explicitly implement server delay, and // paginated calls. Set the ‘showcase-trailer’ metadata key on any method -// to have the values echoed in the response trailers. +// to have the values echoed in the response trailers. Set the +// ‘x-goog-request-params’ metadata key on any method to have the values +// echoed in the response headers. type EchoClient struct { // The internal transport-dependent client. internalClient internalEchoClient @@ -349,7 +351,9 @@ type echoGRPCClient struct { // side streaming, client side streaming, and bidirectional streaming. This // service also exposes methods that explicitly implement server delay, and // paginated calls. Set the ‘showcase-trailer’ metadata key on any method -// to have the values echoed in the response trailers. +// to have the values echoed in the response trailers. Set the +// ‘x-goog-request-params’ metadata key on any method to have the values +// echoed in the response headers. func NewEchoClient(ctx context.Context, opts ...option.ClientOption) (*EchoClient, error) { clientOpts := defaultEchoGRPCClientOptions() if newEchoClientHook != nil { diff --git a/cmd/gapic-showcase/echo.go b/cmd/gapic-showcase/echo.go index 64d9020cb..1fb700bcc 100644 --- a/cmd/gapic-showcase/echo.go +++ b/cmd/gapic-showcase/echo.go @@ -49,6 +49,10 @@ func init() { EchoCmd.Flags().StringVar(&EchoInputSeverity, "severity", "", "The severity to be echoed by the server.") + EchoCmd.Flags().StringVar(&EchoInput.Header, "header", "", "Optional. This field can be set to test the...") + + EchoCmd.Flags().StringVar(&EchoInput.OtherHeader, "other_header", "", "Optional. This field can be set to test the...") + EchoCmd.Flags().StringVar(&EchoInputResponse, "response", "", "Choices: content, error") EchoCmd.Flags().StringVar(&EchoFromFile, "from_file", "", "Absolute path to JSON file containing request payload") diff --git a/go.mod b/go.mod index c03ca2526..df8ac712a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/googleapis/gapic-showcase require ( cloud.google.com/go v0.100.2 github.com/golang/protobuf v1.5.2 - github.com/google/go-cmp v0.5.6 + github.com/google/go-cmp v0.5.7 github.com/googleapis/gax-go/v2 v2.1.1 github.com/googleapis/grpc-fallback-go v0.1.4 github.com/gorilla/mux v1.8.0 @@ -14,7 +14,7 @@ require ( golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c google.golang.org/api v0.65.0 - google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 + google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 google.golang.org/grpc v1.43.0 google.golang.org/protobuf v1.27.1 ) diff --git a/go.sum b/go.sum index 2735f437e..a06dcca8d 100644 --- a/go.sum +++ b/go.sum @@ -170,8 +170,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -739,8 +740,9 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 h1:Et6SkiuvnBn+SgrSYXs/BrUpGB4mbdwt4R3vaPIlicA= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 h1:aCsSLXylHWFno0r4S3joLpiaWayvqd2Mn4iSvx4WZZc= +google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/schema/google/showcase/v1beta1/echo.proto b/schema/google/showcase/v1beta1/echo.proto index 3b722ac05..159d1faa0 100644 --- a/schema/google/showcase/v1beta1/echo.proto +++ b/schema/google/showcase/v1beta1/echo.proto @@ -17,6 +17,7 @@ syntax = "proto3"; import "google/api/annotations.proto"; import "google/api/client.proto"; import "google/api/field_behavior.proto"; +import "google/api/routing.proto"; import "google/longrunning/operations.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; @@ -33,7 +34,9 @@ option ruby_package = "Google::Showcase::V1Beta1"; // side streaming, client side streaming, and bidirectional streaming. This // service also exposes methods that explicitly implement server delay, and // paginated calls. Set the 'showcase-trailer' metadata key on any method -// to have the values echoed in the response trailers. +// to have the values echoed in the response trailers. Set the +// 'x-goog-request-params' metadata key on any method to have the values +// echoed in the response headers. service Echo { // This service is meant to only run locally on the port 7469 (keypad digits // for "show"). @@ -45,6 +48,39 @@ service Echo { post: "/v1beta1/echo:echo" body: "*" }; + option (google.api.routing) = { + routing_parameters{ + field: "header" + } + routing_parameters{ + field: "header" + path_template: "{routing_id=**}" + } + routing_parameters{ + field: "header" + path_template: "{table_name=regions/*/zones/*/**}" + } + routing_parameters{ + field: "header" + path_template: "{super_id=projects/*}/**" + } + routing_parameters{ + field: "header" + path_template: "{table_name=projects/*/instances/*/**}" + } + routing_parameters{ + field: "header" + path_template: "projects/*/{instance_id=instances/*}/**" + } + routing_parameters{ + field: "other_header" + path_template: "{baz=**}" + } + routing_parameters{ + field: "other_header" + path_template: "{qux=projects/*}/**" + } + }; } // This method splits the given content into words and will pass each word back @@ -153,6 +189,12 @@ message EchoRequest { // The severity to be echoed by the server. Severity severity = 3; + + // Optional. This field can be set to test the routing annotation on the Echo method. + string header = 4; + + // Optional. This field can be set to test the routing annotation on the Echo method. + string other_header = 5; } // The response message for the Echo methods. diff --git a/server/services/echo_service.go b/server/services/echo_service.go index ca0af4465..85f085b0b 100644 --- a/server/services/echo_service.go +++ b/server/services/echo_service.go @@ -46,6 +46,7 @@ func (s *echoServerImpl) Echo(ctx context.Context, in *pb.EchoRequest) (*pb.Echo if err != nil { return nil, err } + echoHeaders(ctx) echoTrailers(ctx) return &pb.EchoResponse{Content: in.GetContent(), Severity: in.GetSeverity()}, nil } @@ -57,6 +58,7 @@ func (s *echoServerImpl) Expand(in *pb.ExpandRequest, stream pb.Echo_ExpandServe return err } } + echoStreamingHeaders(stream) if in.GetError() != nil { return status.ErrorProto(in.GetError()) } @@ -70,6 +72,7 @@ func (s *echoServerImpl) Collect(stream pb.Echo_CollectServer) error { for { req, err := stream.Recv() if err == io.EOF { + echoStreamingHeaders(stream) echoStreamingTrailers(stream) return stream.SendAndClose(&pb.EchoResponse{Content: strings.Join(resp, " ")}) } @@ -101,6 +104,7 @@ func (s *echoServerImpl) Chat(stream pb.Echo_ChatServer) error { if s != nil { return s } + echoStreamingHeaders(stream) stream.Send(&pb.EchoResponse{Content: req.GetContent()}) } } @@ -127,6 +131,7 @@ func (s *echoServerImpl) PagedExpand(ctx context.Context, in *pb.PagedExpandRequ responses = append(responses, &pb.EchoResponse{Content: word}) } + echoHeaders(ctx) echoTrailers(ctx) return &pb.PagedExpandResponse{ Responses: responses, @@ -162,6 +167,7 @@ func (s *echoServerImpl) PagedExpandLegacyMapped(ctx context.Context, in *pb.Pag } } + echoHeaders(ctx) echoTrailers(ctx) return &pb.PagedExpandLegacyMappedResponse{ Alphabetized: alphabetized, @@ -207,6 +213,7 @@ func min(x int32, y int32) int32 { } func (s *echoServerImpl) Wait(ctx context.Context, in *pb.WaitRequest) (*lropb.Operation, error) { + echoHeaders(ctx) echoTrailers(ctx) return s.waiter.Wait(in), nil } @@ -217,10 +224,39 @@ func (s *echoServerImpl) Block(ctx context.Context, in *pb.BlockRequest) (*pb.Bl if in.GetError() != nil { return nil, status.ErrorProto(in.GetError()) } + echoHeaders(ctx) echoTrailers(ctx) return in.GetSuccess(), nil } +//echo any provided headers in the metadata +func echoHeaders(ctx context.Context) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return + } + + values := md.Get("x-goog-request-params") + for _, value := range values { + header := metadata.Pairs("x-goog-request-params", value) + grpc.SetHeader(ctx, header) + } +} + +func echoStreamingHeaders(stream grpc.ServerStream) { + md, ok := metadata.FromIncomingContext(stream.Context()) + if !ok { + return + } + values := md.Get("x-goog-request-params") + for _, value := range values { + header := metadata.Pairs("x-goog-request-params", value) + if stream.SetHeader(header) != nil { + return + } + } +} + // echo any provided trailing metadata func echoTrailers(ctx context.Context) { md, ok := metadata.FromIncomingContext(ctx) diff --git a/server/services/echo_service_test.go b/server/services/echo_service_test.go index 97f7f6024..36ddf2bfc 100644 --- a/server/services/echo_service_test.go +++ b/server/services/echo_service_test.go @@ -98,11 +98,12 @@ type mockSTS struct { } func (m *mockSTS) Method() string { return "" } -func (m *mockSTS) SetHeader(md metadata.MD) error { return m.stream.SetHeader(md) } +func (m *mockSTS) SetHeader(md metadata.MD) error { m.stream.SetHeader(md); return nil } func (m *mockSTS) SendHeader(md metadata.MD) error { return m.stream.SendHeader(md) } func (m *mockSTS) SetTrailer(md metadata.MD) error { m.stream.SetTrailer(md); return nil } type mockUnaryStream struct { + head []string trail []string t *testing.T grpc.ServerStream @@ -114,14 +115,19 @@ func (m *mockUnaryStream) Context() context.Context { return nil } func (m *mockUnaryStream) SetTrailer(md metadata.MD) { m.trail = append(m.trail, md.Get("showcase-trailer")...) } -func (m *mockUnaryStream) verify(expectTrailers bool) { - if expectTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) { - m.t.Errorf("Unary stream did not get all expected trailers. Got: %+v", m.trail) +func (m *mockUnaryStream) SetHeader(md metadata.MD) error { + m.head = append(m.head, md.Get("x-goog-request-params")...) + return nil +} +func (m *mockUnaryStream) verify(expectHeadersAndTrailers bool) { + if expectHeadersAndTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) && !reflect.DeepEqual([]string{"showcaseHeader, anotherHeader"}, m.head) { + m.t.Errorf("Unary stream did not get all expected headers and trailers.\nGot these headers: %+v\nGot these trailers: %+v", m.head, m.trail) } } type mockExpandStream struct { exp []string + head []string trail []string t *testing.T pb.Echo_ExpandServer @@ -143,12 +149,17 @@ func (m *mockExpandStream) SetTrailer(md metadata.MD) { m.trail = append(m.trail, md.Get("showcase-trailer")...) } -func (m *mockExpandStream) verify(expectTrailers bool) { +func (m *mockExpandStream) SetHeader(md metadata.MD) error { + m.head = append(m.head, md.Get("x-goog-request-params")...) + return nil +} + +func (m *mockExpandStream) verify(expectHeadersAndTrailers bool) { if len(m.exp) > 0 { m.t.Errorf("Expand did not stream all expected values. %d expected values remaining.", len(m.exp)) } - if expectTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) { - m.t.Errorf("Expand did not get all expected trailers. Got: %+v", m.trail) + if expectHeadersAndTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) && !reflect.DeepEqual([]string{"showcaseHeader", "anotherHeader"}, m.head) { + m.t.Errorf("Expand did not get all expected headers and trailers.\nGot these headers: %+v\nGot these trailers: %+v", m.head, m.trail) } } @@ -195,6 +206,7 @@ func TestExpand_streamErr(t *testing.T) { type mockCollectStream struct { reqs []*pb.EchoRequest + head []string trail []string exp *string t *testing.T @@ -224,13 +236,18 @@ func (m *mockCollectStream) Context() context.Context { return appendTestOutgoingMetadata(context.Background(), &mockSTS{stream: m, t: m.t}) } +func (m *mockCollectStream) SetHeader(md metadata.MD) error { + m.head = append(m.head, md.Get("x-goog-request-params")...) + return nil +} + func (m *mockCollectStream) SetTrailer(md metadata.MD) { m.trail = append(m.trail, md.Get("showcase-trailer")...) } -func (m *mockCollectStream) verify(expectTrailers bool) { - if expectTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) { - m.t.Errorf("Collect did not get all expected trailers. Got: %+v", m.trail) +func (m *mockCollectStream) verify(expectHeadersAndTrailers bool) { + if expectHeadersAndTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) && !reflect.DeepEqual([]string{"showcaseHeader", "anotherHeader"}, m.head) { + m.t.Errorf("Collect did not get all expected trailers.\nGot these headers: %+v\nGot these trailers: %+v", m.head, m.trail) } } @@ -283,12 +300,13 @@ func TestCollect_streamErr(t *testing.T) { server := NewEchoServer() err := server.Collect(stream) if e != err { - t.Error("Expand expected to pass through stream errors.") + t.Error("Collect expected to pass through stream errors.") } } type mockChatStream struct { reqs []*pb.EchoRequest + head []string trail []string curr *pb.EchoRequest t *testing.T @@ -319,13 +337,18 @@ func (m *mockChatStream) Context() context.Context { return appendTestOutgoingMetadata(context.Background(), &mockSTS{stream: m, t: m.t}) } +func (m *mockChatStream) SetHeader(md metadata.MD) error { + m.head = append(m.head, md.Get("x-goog-request-params")...) + return nil +} + func (m *mockChatStream) SetTrailer(md metadata.MD) { m.trail = append(m.trail, md.Get("showcase-trailer")...) } -func (m *mockChatStream) verify(expectTrailers bool) { - if expectTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) { - m.t.Errorf("Chat did not get all expected trailers. Got: %+v", m.trail) +func (m *mockChatStream) verify(expectHeadersAndTrailers bool) { + if expectHeadersAndTrailers && !reflect.DeepEqual([]string{"show", "case"}, m.trail) && !reflect.DeepEqual([]string{"showcaseHeader", "anotherHeader"}, m.head) { + m.t.Errorf("Chat did not get all expected trailers.\nGot these headers: %+v\nGot these trailers: %+v", m.head, m.trail) } } @@ -375,7 +398,7 @@ func TestChat_streamErr(t *testing.T) { server := NewEchoServer() err := server.Chat(stream) if e != err { - t.Error("Expand expected to pass through stream errors.") + t.Error("Chat expected to pass through stream errors.") } } @@ -739,6 +762,6 @@ func TestBlockError(t *testing.T) { func appendTestOutgoingMetadata(ctx context.Context, stream grpc.ServerTransportStream) context.Context { ctx = grpc.NewContextWithServerTransportStream(ctx, stream) - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("showcase-trailer", "show", "showcase-trailer", "case", "trailer", "trail")) + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("showcase-trailer", "show", "showcase-trailer", "case", "trailer", "trail", "x-goog-request-params", "showcaseHeader", "x-goog-request-params", "anotherHeader", "header", "head")) return ctx }