Repository: goadesign/goa
Pull request #2865 (2021-06-05 14:52:30)

Fix the correspondence between 'Error's and 'Response's
issue:https://github.com/goadesign/goa/issues/2867 case 2

If error responses with the same name but different responses are specified in Service and Method, invalid code will be generated.

### design

```go
package design

import (
	. "goa.design/goa/v3/dsl"
)

var _ = Service("calc", func() {
	Error("DivByZero")
	HTTP(func() {
		Response("DivByZero", StatusInternalServerError)
	})
	Method("div", func() {
		Error("DivByZero")
		Payload(func() {
			Field(1, "a", Int, "Left operand")
			Field(2, "b", Int, "Right operand")
			Required("a", "b")
		})
		Result(Int)
		HTTP(func() {
			GET("/div/{a}/{b}")
			Response("DivByZero", StatusBadRequest)
		})
	})
})
```

### goa gen

***before***

```go
func EncodeDivError(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, formatter func(err error) goahttp.Statuser) func(context.Context, http.ResponseWriter, error) error {
	encodeError := goahttp.ErrorEncoder(encoder, formatter)
	return func(ctx context.Context, w http.ResponseWriter, v error) error {
		en, ok := v.(ErrorNamer)
		if !ok {
			return encodeError(ctx, w, v)
		}
		switch en.ErrorName() {
		case "DivByZero":
			res := v.(calc.DivByZero)
			enc := encoder(ctx, w)
			var body interface{}
			if formatter != nil {
				body = formatter(res)
			} else {
				body = NewDivDivByZeroResponseBody(res)
			}
			w.Header().Set("goa-error", res.ErrorName())
			w.WriteHeader(http.StatusBadRequest)
			return enc.Encode(body)
		case "DivByZero":
			res := v.(calc.DivByZero)
			enc := encoder(ctx, w)
			var body interface{}
			if formatter != nil {
				body = formatter(res)
			} else {
				body = NewDivDivByZeroResponseBody(res)
			}
			w.Header().Set("goa-error", res.ErrorName())
			w.WriteHeader(http.StatusInternalServerError)
			return enc.Encode(body)
		default:
			return encodeError(ctx, w, v)
		}
	}
}
```

**after**
```go
func EncodeDivError(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, formatter func(err error) goahttp.Statuser) func(context.Context, http.ResponseWriter, error) error {
	encodeError := goahttp.ErrorEncoder(encoder, formatter)
	return func(ctx context.Context, w http.ResponseWriter, v error) error {
		en, ok := v.(ErrorNamer)
		if !ok {
			return encodeError(ctx, w, v)
		}
		switch en.ErrorName() {
		case "DivByZero":
			res := v.(calc.DivByZero)
			enc := encoder(ctx, w)
			var body interface{}
			if formatter != nil {
				body = formatter(res)
			} else {
				body = NewDivDivByZeroResponseBody(res)
			}
			w.Header().Set("goa-error", res.ErrorName())
			w.WriteHeader(http.StatusBadRequest)
			return enc.Encode(body)
		default:
			return encodeError(ctx, w, v)
		}
	}
}
```


Patch:
diff --git a/codegen/example/example_server.go b/codegen/example/example_server.go
index 5dade147..37d3a6d6 100644
--- a/codegen/example/example_server.go
+++ b/codegen/example/example_server.go
@@ -283,7 +283,7 @@ func main() {
 				}
 				u.Host = net.JoinHostPort(h, *{{ $u.Transport.Type }}PortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":{{ $u.Port }}")
+				u.Host = net.JoinHostPort(u.Host, "{{ $u.Port }}")
 			}
 			handle{{ toUpper $u.Transport.Name }}Server(ctx, u, {{ range $t := $.Server.Transports }}{{ if eq $t.Type $u.Transport.Type }}{{ range $s := $t.Services }}{{ range $.Services }}{{ if eq $s .Name }}{{ if .Methods }}{{ .VarName }}Endpoints, {{ end }}{{ end }}{{ end }}{{ end }}{{ end }}{{ end }}&wg, errc, logger, *dbgF)
 		}
diff --git a/expr/grpc_endpoint.go b/expr/grpc_endpoint.go
index bcb7ba8c..20b59cb7 100644
--- a/expr/grpc_endpoint.go
+++ b/expr/grpc_endpoint.go
@@ -85,31 +85,60 @@ func (e *GRPCEndpointExpr) Prepare() {
 	}
 	e.Response.Prepare()
 
-	// Prepare error response
-	for _, er := range e.GRPCErrors {
-		er.Response.Prepare()
+	// Error -> ResponseError
+	methodErrors := map[string]struct{}{}
+	for _, v := range e.GRPCErrors {
+		methodErrors[v.Name] = struct{}{}
+	}
+	for _, me := range e.MethodExpr.Errors {
+		if _, ok := methodErrors[me.Name]; ok {
+			continue
+		}
+		methodErrors[me.Name] = struct{}{}
+		var found bool
+		for _, v := range e.Service.GRPCErrors {
+			if me.Name == v.Name {
+				e.GRPCErrors = append(e.GRPCErrors, v.Dup())
+				found = true
+				break
+			}
+		}
+		if found {
+			continue
+		}
+		// Lookup undefined GRPC errors in API.
+		for _, v := range Root.API.GRPC.Errors {
+			if me.Name == v.Name {
+				e.GRPCErrors = append(e.GRPCErrors, v.Dup())
+			}
+		}
 	}
-
-	// Inherit error only if it doesn't already exist in the endpoint errors
-	inherit := func(r *GRPCErrorExpr) {
-		found := false
-		for _, er := range e.GRPCErrors {
-			if er.Name == r.Name {
+	// Inherit GRPC errors from service if the error has not added.
+	for _, se := range e.Service.ServiceExpr.Errors {
+		if _, ok := methodErrors[se.Name]; ok {
+			continue
+		}
+		var found bool
+		for _, resp := range e.Service.GRPCErrors {
+			if se.Name == resp.Name {
 				found = true
+				e.GRPCErrors = append(e.GRPCErrors, resp.Dup())
 				break
 			}
 		}
 		if !found {
-			r.Response.Prepare()
-			e.GRPCErrors = append(e.GRPCErrors, r.Dup())
+			for _, ae := range Root.API.GRPC.Errors {
+				if se.Name == ae.Name {
+					e.GRPCErrors = append(e.GRPCErrors, ae.Dup())
+					break
+				}
+			}
 		}
 	}
-	// Inherit gRPC errors from service and root
-	for _, r := range e.Service.GRPCErrors {
-		inherit(r)
-	}
-	for _, r := range Root.API.GRPC.Errors {
-		inherit(r)
+
+	// Prepare responses
+	for _, er := range e.GRPCErrors {
+		er.Response.Prepare()
 	}
 }
 
diff --git a/expr/http_endpoint.go b/expr/http_endpoint.go
index 4ea8c18d..d70b9460 100644
--- a/expr/http_endpoint.go
+++ b/expr/http_endpoint.go
@@ -271,24 +271,52 @@ func (e *HTTPEndpointExpr) Prepare() {
 		e.Responses = []*HTTPResponseExpr{{StatusCode: status}}
 	}
 
-	// Inherit HTTP errors from service
-	for _, r := range e.Service.HTTPErrors {
-		e.HTTPErrors = append(e.HTTPErrors, r.Dup())
+	// Error -> ResponseError
+	methodErrors := map[string]struct{}{}
+	for _, v := range e.HTTPErrors {
+		methodErrors[v.Name] = struct{}{}
 	}
-
-	// Lookup undefined HTTP errors in API.
-	for _, err := range e.MethodExpr.Errors {
-		found := false
-		for _, herr := range e.HTTPErrors {
-			if err.Name == herr.Name {
+	for _, me := range e.MethodExpr.Errors {
+		if _, ok := methodErrors[me.Name]; ok {
+			continue
+		}
+		methodErrors[me.Name] = struct{}{}
+		var found bool
+		for _, v := range e.Service.HTTPErrors {
+			if me.Name == v.Name {
+				e.HTTPErrors = append(e.HTTPErrors, v.Dup())
 				found = true
 				break
 			}
 		}
+		if found {
+			continue
+		}
+		// Lookup undefined HTTP errors in API.
+		for _, v := range Root.API.HTTP.Errors {
+			if me.Name == v.Name {
+				e.HTTPErrors = append(e.HTTPErrors, v.Dup())
+			}
+		}
+	}
+	// Inherit HTTP errors from service if the error has not added.
+	for _, se := range e.Service.ServiceExpr.Errors {
+		if _, ok := methodErrors[se.Name]; ok {
+			continue
+		}
+		var found bool
+		for _, resp := range e.Service.HTTPErrors {
+			if se.Name == resp.Name {
+				found = true
+				e.HTTPErrors = append(e.HTTPErrors, resp.Dup())
+				break
+			}
+		}
 		if !found {
-			for _, herr := range Root.API.HTTP.Errors {
-				if herr.Name == err.Name {
-					e.HTTPErrors = append(e.HTTPErrors, herr.Dup())
+			for _, ae := range Root.API.HTTP.Errors {
+				if se.Name == ae.Name {
+					e.HTTPErrors = append(e.HTTPErrors, ae.Dup())
+					break
 				}
 			}
 		}


Test patch:
diff --git a/codegen/example/testdata/example_server_code.go b/codegen/example/testdata/example_server_code.go
index 2dc8df21..d5e5056a 100644
--- a/codegen/example/testdata/example_server_code.go
+++ b/codegen/example/testdata/example_server_code.go
@@ -78,7 +78,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -104,7 +104,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -201,7 +201,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -227,7 +227,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -324,7 +324,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -350,7 +350,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":443")
+				u.Host = net.JoinHostPort(u.Host, "443")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -376,7 +376,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -402,7 +402,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -516,7 +516,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -551,7 +551,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":443")
+				u.Host = net.JoinHostPort(u.Host, "443")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -630,7 +630,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, &wg, errc, logger, *dbgF)
 		}
@@ -727,7 +727,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -753,7 +753,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -854,7 +854,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -880,7 +880,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -976,7 +976,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1003,7 +1003,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":443")
+				u.Host = net.JoinHostPort(u.Host, "443")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1116,7 +1116,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1145,7 +1145,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":443")
+				u.Host = net.JoinHostPort(u.Host, "443")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1241,7 +1241,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1267,7 +1267,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceWithSpacesEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1363,7 +1363,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1460,7 +1460,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1561,7 +1561,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *httpPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":80")
+				u.Host = net.JoinHostPort(u.Host, "80")
 			}
 			handleHTTPServer(ctx, u, serviceEndpoints, anotherServiceEndpoints, &wg, errc, logger, *dbgF)
 		}
@@ -1587,7 +1587,7 @@ const (
 				}
 				u.Host = net.JoinHostPort(h, *grpcPortF)
 			} else if u.Port() == "" {
-				u.Host = net.JoinHostPort(u.Host, ":8080")
+				u.Host = net.JoinHostPort(u.Host, "8080")
 			}
 			handleGRPCServer(ctx, u, serviceEndpoints, &wg, errc, logger, *dbgF)
 		}