Goa v3.2.0

Announcing Goa v3.2.0

I am very excited to announce the release of Goa v3.2.0! This release includes a few key improvements as well as many bug fixes.

HTTP Cookies

v3.2.0 adds native support for HTTP cookies in Goa designs. Prior to this release cookies could be read and written by Goa services using HTTP middlewares that would read (or inject) the values from (into) the context. For example:

// NOT NECESSARY ANYMORE
package cookie

type cookieKeyType int // private so key is unique
var CookieKey cookieKeyType = iota + 1

// ReadCookie returns a HTTP middleware that reads the value of the cookie with
// the given name and adds it to the request context under cookiectx.CookieKey.
func ReadCookie(name string) func(http.Handler) http.Handler {
    return func(h http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            c, err := req.Cookie(name)
            if err != nil {
                goto skip
            } 
            ctx := context.WithValue(r.Context(), cookiectx.CookieKey, c.Value)
            r = r.WithContext(ctx)
        skip:
            h.ServeHTTP(w, r)
        })
    }
}

// Usage:
// Mount middleware when creating the server:
srv := calcsvr.New(calcEndpoints, mux, dec, enc, eh, nil)
srv.Use(cookie.ReadCookie("session_uid"))

// Use the value from the context in the service methods:
func (s *Service) Endpoint(ctx context.Context, p *Payload) (*Result, error) {
    sessionUID := ctx.Value(cookie.CookieKey)
    // ...
}

Goa now makes it possible to provide the same functionality by simply adding the following to the HTTP expression of the method:

// DO THIS INSTEAD
var _ = Service("session", func() {
    Method("use_session", func() {
        Payload(func() {
            Attribute("session_uid", String, "Session ID")
        })
        HTTP(func() {
            GET("/")
            Cookie("session_uid") // Load session ID from "SID" cookie
        })
    })
})

Note that the generated code does NOT make use of the context but instead properly decodes the cookie into the payload (server side) or result (client side). Cookie also makes it possible to write cookies into HTTP responses (and read them client side).

See the example and DSL doc for additional information.

OpenAPI v3

This release also adds support for the OpenAPI v3.0.3 specification. The goa gen tool now generates openapi3.yaml and openapi3.json files under the gen/http/ folder. This is in addition to the already generated openapi.yaml and openapi.json files that contain the OpenAPI v2 specification.

A big thank you to Nitin Mohan and Neenu Ann Varghese for their help in putting this together!

API Level Errors

As a convenience Goa now makes it possible to define the transport specific details of errors in the API expression. This makes it possible to provide such details once and reuse them in multiple methods. Note that the services and/or methods still need to explicitly declare the error (similar semantic to Reference). For example:

var _ = API("test", func() {
    Error("bad_request")
    HTTP(func() {
        Response(StatusBadRequest, "bad_request")
    })
})

Service("Service", func() {
    Method("Method", func() {
        Error("bad_request") // No need to define transport details again
        Error("internal_error") // Method specific error, requires transport details
        HTTP(func() {
            GET("/one/two")
            Response("internal_error", StatusInternalServerError) // transport details
        })
    })
})

And a lot more

The complete list of changes from v3.1.0 to v3.2.0 includes many other improvements and fixes, in particular:

  • Nitin Mohan fixed an issue with cyclic result type definitions that could cause the goa tool to crash. (#2517)
  • @ikawaha fixed an issue with trailing slashes appearing in the generated path functions. The fix makes sure that if a relative path ends with / then it is preserved except in the case where the relative path is just /. In this case the slash is not added to the base path. The slash can be added in this case by using ./ instead of /. (#2530) (#2557).
  • Taichi Sasaki helped finalize the setup of GitHub actions as a replacement for TravisCI on the v3 branch. (#2529) (#2533)
  • @achow-flexera helped fix issues with the recently added SkipRequestEncodeDecode. (#2532) (#2543)
  • Taichi Sasaki also fixed the value of the Content-Type header used in HTTP responses that correspond to errors so that it would correctly honor any content type specified in the design via ContentType. (#2566)
  • Nitin Mohan fixed an issue where the generated clients could not properly set the authorization bearer token. (#2563)
  • Nitin Mohan also fixed an issue where the conversions between snake_case and CamelCase could be inconsistent which could affect ConvertTo and CreateFrom. (#2568)
  • @mrsndmn fixed an issue where the struct:field:name tag would not work when used in conjunction with error:field:name. (#2596)
  • Yamamoto Shun fixed an issue with the example code generated to handle graceful shutdown. (#2603)
  • Yamamoto Shun also made sure the example code properly handles SIGTERM on top of SIGINT to trigger graceful shutdown. (#2604)

Thank you to all the contributors! Come by the Goa slack channel (invite here) for any question or just to say hi!