goadesign / goadesign/goa

bug/design: forwarding over `formatter` takes wrong type

Open
#3,718 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
6.1k
Forks
583
Avg merge
2d 10h
Merged PRs (30d)
15

Description

I suspect there is an error in generation of the Encode/Decode...Error functions. See the example below.

When I look at the generated code, I see that `formatter(ctx, res)` gets the casted type `res` (`cm.CustodianError`) (our error type on the method).

- This is problematic when you return a wrapped error from the endpoint: i.e. wrapping `cm.CustodianError` to be able to do custom stuff in a custom `formatter` (e.g. `v` is an `error` which `Unwrap`s to `cm.CustodianError` etc).

- IMO: it should receive `v`, before `Unwrap` in `As(v, &res)` such that the `formatter` has a chance to actually read a wrapped type.
- From a design perspective I dont understand yet why `body = formatter(ctx, res)` returns a `goahttp.Statuser` because
the returned interface `goahttp.Statuser` is not used as the status is forcefully set (see below):
Maybe `formatter` needs to be extended to be something like:

```go
type ErrorFormatter interface {
MarshalToStatuser(ctx context.Context, v any) goahttp.Statuser
Marshal(ctx context.Context, v any) any
}
```

- Also problematic is that `errors.As(v, &res)` is not checked and just panics (should be a debug assert).

Encode/Decode Generated:

```go
func EncodeGetContractError(encoder func(context.Context, http.ResponseWriter) goahttp.Encoder, formatter func(ctx context.Context, 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 {
var en goa.GoaErrorNamer
if !errors.As(v, &en) {
return encodeError(ctx, w, v)
}
switch en.GoaErrorName() {
case "cm#not-found":
var res *cm.CustodianError
errors.As(v, &res)
enc := encoder(ctx, w)
var body any
if formatter != nil {
body = formatter(ctx, res)
} else {
body = NewGetContractCmNotFoundResponseBody(res)
}
w.Header().Set("goa-error", res.GoaErrorName())
w.WriteHeader(http.StatusNotFound)
return enc.Encode(body)

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.