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 that package. The controllers
package
contains the API business logic and is the only non-generated directory apart from the design
package.
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:
-
The scaffold is code that is produced once. It consists of main.go
, the controller
files and the client main.go
. These files are generated to help the project get started quickly
however they should be edited, moved, renamed etc. as needed.
-
The generated code is produced each time entirely from scratch. This is the vast majority of the
code that is outputed by goagen
and the parts that change the most as the design evolves. This
includes the app
package and its test
subpackage, the swagger files and the client files.
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.