From cf9a76924518bab33d406af740b5998ef2c86f9f Mon Sep 17 00:00:00 2001 From: Victor Chudnovsky Date: Fri, 8 Mar 2024 13:51:25 -0800 Subject: [PATCH] feat(errors): allow Echo to auto-populate fake error details This is useful for testing client responses to errors. This can be tested over REST with curl -i -X POST http://localhost:7469/v1beta1/echo:echo \ -d'{"error": {"code": 8}, "generateErrorDetails": true}' \ -H "Content-Type: application/json" -H "X-Goog-Api-Client: rest/0.0.0 gapic/0.0.0" --- schema/google/showcase/v1beta1/echo.proto | 5 ++ server/services/echo_service.go | 92 ++++++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/schema/google/showcase/v1beta1/echo.proto b/schema/google/showcase/v1beta1/echo.proto index bae212a5b..4ce548603 100644 --- a/schema/google/showcase/v1beta1/echo.proto +++ b/schema/google/showcase/v1beta1/echo.proto @@ -215,6 +215,11 @@ message EchoRequest { string request_id = 7 [ (google.api.field_info).format = UUID4 ]; + + // If `error` is set, then this flag causes the Showcase server to append + // multiple fake error details in the error response. This is useful for + // testing client behavior on error responses. + bool generate_error_details = 8; } // The response message for the Echo methods. diff --git a/server/services/echo_service.go b/server/services/echo_service.go index fe759d3c3..ec73bef53 100644 --- a/server/services/echo_service.go +++ b/server/services/echo_service.go @@ -33,6 +33,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" anypb "google.golang.org/protobuf/types/known/anypb" + durationpb "google.golang.org/protobuf/types/known/durationpb" ) // NewEchoServer returns a new EchoServer for the Showcase API. @@ -44,10 +45,95 @@ type echoServerImpl struct { waiter server.Waiter } +func PopulateFakeErrorDetails(grpcStatus *status.Status) (*status.Status, error) { + return grpcStatus.WithDetails( + &errdetails.ErrorInfo{ + Reason: "(showcase:ErrorInfo) RPC requested we populate all error details", + Domain: "googleapis.com", + }, + &errdetails.RetryInfo{ + RetryDelay: &durationpb.Duration{ + Seconds: 3, + Nanos: 14159265, + }, + }, + &errdetails.DebugInfo{ + StackEntries: []string{"frame 1", "frame 2", "frame3"}, + Detail: "(showcase:DebugInfo) sample stack frames", + }, + &errdetails.QuotaFailure{ + Violations: []*errdetails.QuotaFailure_Violation{ + { + Subject: "(showcase:QuotaFailure) showcase-testing:fake errors", + Description: "This is fake quota error 0", + }, + { + Subject: "(showcase:QuotaFailure) showcase-testing:more fake errors", + Description: "This is fake quota error 1", + }, + }, + }, + &errdetails.PreconditionFailure{ + Violations: []*errdetails.PreconditionFailure_Violation{ + { + Type: "Fake Type 0", + Subject: "(showcase:PreconditionFailure) showcase-testing:fake errors", + Description: "This is fake precondition error 0", + }, + { + Type: "Fake Type 1", + Subject: "(showcase:PreconditionFailure) showcase-testing:fake errors", + Description: "This is fake precondition error 1", + }, + }, + }, + &errdetails.BadRequest{ + FieldViolations: []*errdetails.BadRequest_FieldViolation{ + { + Field: "field 0", + Description: "(showcase:BadRequest) description 0", + }, + { + Field: "field 1", + Description: "(showcase:BadRequest) description 1", + }, + }, + }, + &errdetails.Help{ + Links: []*errdetails.Help_Link{ + { + Description: "(showcase:Help) Description 0", + Url: "URL 0", + }, + { + Description: "(showcase:Help) Description 1", + Url: "URL 1", + }, + }, + }, + &errdetails.LocalizedMessage{ + Locale: "en-US", + Message: "(showcase:LocalizedMessage) Some message for the user", + }, + ) +} + func (s *echoServerImpl) Echo(ctx context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) { - err := status.ErrorProto(in.GetError()) - if err != nil { - return nil, err + requestedError := in.GetError() + if requestedError != nil { + if in.GenerateErrorDetails { + returnDetails, err := PopulateFakeErrorDetails(status.FromProto(requestedError)) + if err != nil { + return nil, err + } + if returnDetails != nil { + return nil, returnDetails.Err() + } + } + err := status.ErrorProto(requestedError) + if err != nil { + return nil, err + } } echoHeaders(ctx) echoTrailers(ctx)