Repository: goadesign/goa
Pull request #3025 (2022-03-07 19:05:11)

Properly handle wrapped errors


Patch:
diff --git a/grpc/codegen/server.go b/grpc/codegen/server.go
index e6bc823d..26b6e4d0 100644
--- a/grpc/codegen/server.go
+++ b/grpc/codegen/server.go
@@ -270,7 +270,8 @@ func (s *{{ .ServerStruct }}) {{ .Method.VarName }}(
 		{{- range .Errors }}
 			case {{ printf "%q" .Name }}:
 				{{- if .Response.ServerConvert }}
-					er := err.({{ .Response.ServerConvert.SrcRef }})
+					var er {{ .Response.ServerConvert.SrcRef }}
+					errors.As(err, &er)
 				{{- end }}
 				return {{ if not $.ServerStream }}nil, {{ end }}goagrpc.NewStatusError({{ .Response.StatusCode }}, err, {{ if .Response.ServerConvert }}{{ .Response.ServerConvert.Init.Name }}({{ range .Response.ServerConvert.Init.Args }}{{ .Name }}, {{ end }}){{ else }}goagrpc.NewErrorResponse(err){{ end }})
 		{{- end }}
diff --git a/http/codegen/server.go b/http/codegen/server.go
index a884ec3a..96532a88 100644
--- a/http/codegen/server.go
+++ b/http/codegen/server.go
@@ -1211,7 +1211,8 @@ func {{ .ErrorEncoder }}(encoder func(context.Context, http.ResponseWriter) goah
 	{{- range $gerr := .Errors }}
 	{{- range $err := .Errors }}
 		case {{ printf "%q" .Name }}:
-			res := v.({{ $err.Ref }})
+			var res {{ $err.Ref }}
+			errors.As(v, &res)
 			{{- with .Response}}
 				{{- if .ContentType }}
 					ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "{{ .ContentType }}")


Test patch:
diff --git a/grpc/codegen/testdata/server_interface_code.go b/grpc/codegen/testdata/server_interface_code.go
index ead53a70..ffcd59a7 100644
--- a/grpc/codegen/testdata/server_interface_code.go
+++ b/grpc/codegen/testdata/server_interface_code.go
@@ -64,13 +64,16 @@ func (s *Server) MethodUnaryRPCWithErrors(ctx context.Context, message *service_
 			case "timeout":
 				return nil, goagrpc.NewStatusError(codes.Canceled, err, goagrpc.NewErrorResponse(err))
 			case "internal":
-				er := err.(*serviceunaryrpcwitherrors.AnotherError)
+				var er *serviceunaryrpcwitherrors.AnotherError
+				errors.As(err, &er)
 				return nil, goagrpc.NewStatusError(codes.Unknown, err, NewMethodUnaryRPCWithErrorsInternalError(er))
 			case "bad_request":
-				er := err.(*serviceunaryrpcwitherrors.AnotherError)
+				var er *serviceunaryrpcwitherrors.AnotherError
+				errors.As(err, &er)
 				return nil, goagrpc.NewStatusError(codes.InvalidArgument, err, NewMethodUnaryRPCWithErrorsBadRequestError(er))
 			case "custom_error":
-				er := err.(*serviceunaryrpcwitherrors.ErrorType)
+				var er *serviceunaryrpcwitherrors.ErrorType
+				errors.As(err, &er)
 				return nil, goagrpc.NewStatusError(codes.Unknown, err, NewMethodUnaryRPCWithErrorsCustomErrorError(er))
 			}
 		}
diff --git a/http/codegen/testdata/error_encoder_code.go b/http/codegen/testdata/error_encoder_code.go
index b48481b6..8313d318 100644
--- a/http/codegen/testdata/error_encoder_code.go
+++ b/http/codegen/testdata/error_encoder_code.go
@@ -12,14 +12,16 @@ func EncodeMethodPrimitiveErrorResponseError(encoder func(context.Context, http.
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(serviceprimitiveerrorresponse.BadRequest)
+			var res serviceprimitiveerrorresponse.BadRequest
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			body := res
 			w.Header().Set("goa-error", res.ErrorName())
 			w.WriteHeader(http.StatusBadRequest)
 			return enc.Encode(body)
 		case "internal_error":
-			res := v.(serviceprimitiveerrorresponse.InternalError)
+			var res serviceprimitiveerrorresponse.InternalError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			body := res
 			w.Header().Set("goa-error", res.ErrorName())
@@ -44,7 +46,8 @@ func EncodeMethodPrimitiveErrorInResponseHeaderError(encoder func(context.Contex
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(serviceprimitiveerrorinresponseheader.BadRequest)
+			var res serviceprimitiveerrorinresponseheader.BadRequest
+			errors.As(v, &res)
 			{
 				val := string(res)
 				string_s := val
@@ -54,7 +57,8 @@ func EncodeMethodPrimitiveErrorInResponseHeaderError(encoder func(context.Contex
 			w.WriteHeader(http.StatusBadRequest)
 			return nil
 		case "internal_error":
-			res := v.(serviceprimitiveerrorinresponseheader.InternalError)
+			var res serviceprimitiveerrorinresponseheader.InternalError
+			errors.As(v, &res)
 			{
 				val := int(res)
 				int_s := strconv.Itoa(val)
@@ -82,7 +86,8 @@ func EncodeMethodAPIPrimitiveErrorResponseError(encoder func(context.Context, ht
 		}
 		switch en.ErrorName() {
 		case "internal_error":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			var body interface{}
 			if formatter != nil {
@@ -94,7 +99,8 @@ func EncodeMethodAPIPrimitiveErrorResponseError(encoder func(context.Context, ht
 			w.WriteHeader(http.StatusInternalServerError)
 			return enc.Encode(body)
 		case "bad_request":
-			res := v.(serviceapiprimitiveerrorresponse.BadRequest)
+			var res serviceapiprimitiveerrorresponse.BadRequest
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			body := res
 			w.Header().Set("goa-error", res.ErrorName())
@@ -118,7 +124,8 @@ func EncodeMethodDefaultErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			var body interface{}
 			if formatter != nil {
@@ -147,7 +154,8 @@ func EncodeMethodDefaultErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "application/xml")
 			enc := encoder(ctx, w)
 			var body interface{}
@@ -177,7 +185,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "internal_error":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			var body interface{}
 			if formatter != nil {
@@ -189,7 +198,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 			w.WriteHeader(http.StatusInternalServerError)
 			return enc.Encode(body)
 		case "bad_request":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			var body interface{}
 			if formatter != nil {
@@ -218,7 +228,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "internal_error":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			enc := encoder(ctx, w)
 			var body interface{}
 			if formatter != nil {
@@ -230,7 +241,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 			w.WriteHeader(http.StatusInternalServerError)
 			return enc.Encode(body)
 		case "bad_request":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "application/xml")
 			enc := encoder(ctx, w)
 			var body interface{}
@@ -260,7 +272,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(*servicenobodyerrorresponse.StringError)
+			var res *servicenobodyerrorresponse.StringError
+			errors.As(v, &res)
 			if res.Header != nil {
 				w.Header().Set("Header", *res.Header)
 			}
@@ -285,7 +298,8 @@ func EncodeMethodServiceErrorResponseError(encoder func(context.Context, http.Re
 		}
 		switch en.ErrorName() {
 		case "bad_request":
-			res := v.(*servicenobodyerrorresponse.StringError)
+			var res *servicenobodyerrorresponse.StringError
+			errors.As(v, &res)
 			ctx = context.WithValue(ctx, goahttp.ContentTypeKey, "application/xml")
 			if res.Header != nil {
 				w.Header().Set("Header", *res.Header)
@@ -312,7 +326,8 @@ func EncodeMethodEmptyErrorResponseBodyError(encoder func(context.Context, http.
 		}
 		switch en.ErrorName() {
 		case "internal_error":
-			res := v.(*goa.ServiceError)
+			var res *goa.ServiceError
+			errors.As(v, &res)
 			w.Header().Set("Error-Name", res.Name)
 			w.Header().Set("Goa-Attribute-Id", res.ID)
 			w.Header().Set("Goa-Attribute-Message", res.Message)
@@ -335,7 +350,8 @@ func EncodeMethodEmptyErrorResponseBodyError(encoder func(context.Context, http.
 			w.WriteHeader(http.StatusInternalServerError)
 			return nil
 		case "not_found":
-			res := v.(serviceemptyerrorresponsebody.NotFound)
+			var res serviceemptyerrorresponsebody.NotFound
+			errors.As(v, &res)
 			{
 				val := string(res)
 				inHeaders := val
@@ -363,7 +379,8 @@ func EncodeMethodEmptyCustomErrorResponseBodyError(encoder func(context.Context,
 		}
 		switch en.ErrorName() {
 		case "internal_error":
-			res := v.(*serviceemptycustomerrorresponsebody.Error)
+			var res *serviceemptycustomerrorresponsebody.Error
+			errors.As(v, &res)
 			w.Header().Set("goa-error", res.ErrorName())
 			w.WriteHeader(http.StatusInternalServerError)
 			return nil