Home

Awesome

goa Cellar

The goa winecellar service provides an example for the goa web application framework.

The service implements an API for managing wine bottles. The service is multitenant: bottles are created in the context of an account. At this time the database is emulated with a in-memory hash. An instance of this example is hosted at http://cellar.goa.design.

Usage

Calling the Hosted Service

Using the excellent httpie client:

Listing bottles in account 1:

http http://cellar.goa.design/cellar/accounts/1/bottles
HTTP/1.1 200 OK
Content-Length: 707
Content-Type: application/vnd.goa.example.bottle+json; type=collection; charset=utf-8
Date: Sun, 06 Dec 2015 09:06:10 GMT
Server: Google Frontend
Vary: Origin

[
    {
        "href": "/cellar/accounts/1/bottles/100",
        "id": 100,
        "links": {
            "account": {
                "href": "/cellar/accounts/1",
                "id": 1,
                "name": "account 1"
            }
        },
        "name": "Number 8",
        "rating": 4,
        "varietal": "Merlot",
        "vineyard": "Asti Winery",
        "vintage": 2012
    },
# ...

Creating a new account:

http POST http://cellar.goa.design/cellar/accounts name=sonoma created_by=me
HTTP/1.1 201 Created
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 06 Dec 2015 09:08:55 GMT
Location: /cellar/accounts/3
Server: Google Frontend
Vary: Origin

Showing the newly created account:

http  http://cellar.goa.design/cellar/accounts/3
HTTP/1.1 200 OK
Content-Length: 66
Content-Type: application/vnd.goa.example.account+json; charset=utf-8
Date: Sun, 06 Dec 2015 09:10:09 GMT
Server: Google Frontend
Vary: Origin

{
    "created_at": "",
    "created_by": "me",
    "href": "",
    "id": 3,
    "name": "sonoma"
}

Running Locally

Assuming a working Go setup:

go install github.com/goadesign/goa-cellar
goa-cellar

Once running goa-cellar listens on port 8081. The service serves the generated JavaScript example at /ui, open http://localhost:8081/ui in a browser to display it. This example loads the generated JavaScript client from the service. It then calls the JavaScript client ListBottle function which makes a request to list the bottles to the service and displayes the results.

Generated vs. Non Generated Code

One thing to be aware of when looking at the example is that most of it is generated code. The design package contains the DSL that describes the winecellar API. The contents of the app, client, js, schema and swagger folders are all completely generated by the goagen tool from the design package. The only non-generated directories are:

The generated documentation for the example API is available on swagger.goa.design.

FAQ

Why are the controllers in a "controllers" package instead of the default "main" package?

goagen generates both main.go and the controller files in the main package by default. This example splits the two and keeps main.go in the main package but moves the controller implementations (account.go and bottle.go) to the controllers package. The files were moved and edited "manually".

This is to illustrate an important aspect of code generation in goa: goagen generates two types of outputs:

While it is possible to control where both the scaffold and generated code is produced (with the --output flag) it is not possible to edit the generated code. Any change made to these files will be overridden next time goagen is invoked. This is to enforce a clean separation between generated code and user code. Generated code calls the user code via explicit Go interfaces so that it's clear what needs to be done when the generated code differs as a result of changes in the design.

Where are the tests?

The controllers package illustrates how to write component level tests. These tests setup input data, call the generated test helpers which package that data into HTTP requests and calls the actual controller functions. The test helpers retrieve the written responses, deserialize them, validate the generated data structures (against the validations written in the design) and make them available to the tests. This makes it simple to use table driven tests that setup various kinds of inputs and validate the resulting responses. See the file account_test.go.