goagen, the goa Tool
Code Generation Tool
goagen
is a tool that generates various artifacts from a goa design package.
Install it with:
go install github.com/goadesign/goa/goagen
Each type of artifact is associated with a goagen
command that exposes it own set of flags.
Internally these commands map to “generators” that contain the logic for generating the artifacts.
It works something like this:
goagen
parses the command line to determine the type of output desired and invokes the appropriate generator.- The generator writes the code of the tool that will produce the final output to a temporary directory.
- The tool composed of the design package and the tool code is compiled in the temporary directory.
- The tool is run, it traverses the design data structures to write the final output.
Each generator is exposed via a command of the goagen
tool, goagen --help
lists all the available
commands. These are:
app
: generates the service boilerplate code including controllers, contexts, media types and user types.main
: generates a skeleton file for each resource controller as well as a defaultmain
.client
: generates an API client Go package and tool.js
: generates a JavaScript API client.swagger
: generates the API Swagger specification.schema
: generates the API Hyper-schema JSON.gen
: invokes a third party generator.controller
: generates controller scaffolding for each resource.bootstrap
: invokes theapp
,main
,client
andswagger
generators.
Working With goagen: Scaffold vs. Generated Code
As the design evolves and goagen
needs to be run multiple times it is important to understand
the distinction between scaffold and generated outputs.
The scaffold code generated by the main
command is generated once as a way to
get started quickly. This code is meant to be edited, tested and in general
handled like any other file written “manually”. By default, goagen main
does
not override the scaffold files if they already exist, although it can update
existing scaffold files if the --regen
argument is passed. The main
package
of the client tool generated by the client
command is also scaffold.
The generated artifacts consisting of the outputs of all the other commands (and the output
of the client
command with the exception of the tool main
package) should not and cannot
be edited. The artifacts are regenerated from scratch every time goagen
is run. User code
uses the generated Go packages by importing them and consuming the public functions and data
structures like any other Go package.
Common flags
The following flags apply to all the goagen
commands:
--design|-d=DESIGN
defines the Go package path to the service design package.--out|-o=OUT
specifies where to generate the files, defaults to the current directory.--debug
enablesgoagen
debug. This causesgoagen
to print the content of the temporary files and to leave them around.--help
prints contextual help.
Contexts and Controllers: goagen app
The app
command is arguably the most critical. It generates all the supporting code for the
goa service. The command also generates a test
sub-package that contain test helpers, one per
controller action and view they return. These helpers make it easy to test the action
implementations by invoking them with controlled values and validating the returned media types.
This command supports a couple of additional flags:
--pkg=app
specifies the name of the generated Go package, defaults toapp
. That’s also the name of the subdirectory that gets created to store the generated Go files.--notest
prevents the generation of the the test helpers.
This command always deletes and re-creates any pre-existing directory with the same name. The idea being that these files should never be edited.
Scaffolding: goagen main
The main
command helps bootstrap a new goa service by generating a default main.go
as well as a
default (empty) implementation for each resource controller defined in the design package. By
default, this command only generates the files if they don’t exist in the output directory. This
command accepts two additional flags:
--force
causes the files to be generated even if files with the same name already exist, overwriting the files with new empty scaffolding.--regen
causes the scaffolding to be regenerated in existing files, leaving controller implementations in place.
A typical workflow would include bootstrapping your service with goagen main
,
then as new resources and actions are added to the design, running goagen main --regen
to generate the new empty controller implementations while retaining
your existing controllers.
Note: in order for --regen
to function, controller implementations MUST be
placed between the comments:
// ControllerName_Action: start_implement
... your logic ...
// ControllerName_Action: end_implement
The regeneration code relies on these comments to locate non-scaffold code it should retain.
Client Package and Tool: goagen client
The client
command generates both an API client package and a CLI tool. The client package
implements a Client
object that exposes one method for each resource action. It also exposes
methods to create the corresponding http request objects without sending them. The generated code of
the CLI tool leverages the package to make the API requests to the service.
The Client
object is configured to use request signers that get invoked prior to sending the
request if security is enabled for the Action (that is if the action DSL makes use of the Security
function). The signers modify the request to include auth headers for example. goa comes with
signers that implement
basic auth,
JWT auth,
API key and a subset of
OAuth2.
JavaScript: goagen js
The js
command generates a JavaScript API client suitable for both client-side and server-side
applications. The generated code defines an anonymous AMD module and relies on the
axios promise-based JavaScript library for making the actual
HTTP requests.
The generated module wraps the axios
client and adds API specific functions, for example:
// List all bottles in account optionally filtering by year
// path is the request path, the format is "/cellar/accounts/:accountID/bottles"
// years is used to build the request query string.
// config is an optional object to be merged into the config built by the function prior to making the request.
// The content of the config object is described here: https://github.com/mzabriskie/axios#request-api
// This function returns a promise which raises an error if the HTTP response is a 4xx or 5xx.
client.listBottle = function (path, years, config) {
cfg = {
timeout: timeout,
url: urlPrefix + path,
method: 'get',
params: {
years: years
},
responseType: 'json'
};
if (config) {
cfg = utils.merge(cfg, config);
}
return client(cfg);
}
The generated client module can be loaded using requirejs
:
requirejs.config({
paths: {
axios: '/js/axios.min',
client: '/js/client'
}
});
requirejs(['client'], function (client) {
client().listBottle ("/cellar/accounts/440/bottles", 317)
.then(function (resp) {
// All good, use resp
})
.catch(function (resp) {
// Woops, request failed or returned 4xx or 5xx.
});
});
The code above assumes that the generated files client.js
and axios.min.js
are both
served from /js
. The resp
value returned to the promise is an object with the following
fields:
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` is the headers that the server responded with
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {}
}
The generator also produces an example HTML and controller that can be mounted on a goa service to
quickly test the JavaScript. Simply import the js
Go package in your service main and mount the
controller. The example HTML is served under /js
so that loading this path in a browser will
trigger the generated JavaScript.
Swagger: goagen swagger
The swagger
command generates a Swagger specification of the API. The command
does not accept additional flags. It generates both the Swagger JSON and YAML. The generated files
can be served from the API itself using a file server defined in the design with
Files
JSON Schema: goagen schema
The schema
command generates a
Heroku-like JSON
hyper-schema representation of the API. It generates both the JSON as well as a controller that can
be mounted on the goa service to serve it under /schema.json
.
Plugins: goagen gen
The gen
command makes it possible to invoke goagen
plugins.
This command accepts one flag:
--pkg-path=PKG-PATH
specifies the Go package import path to the plugin package.
Refer to the Generator Plugins section for details on goa plugins.
Scaffolding: goagen controller
The controller
command generates a default (empty) implementation for each resource
controller defined in the design package. By default this command only generates the files
if they don’t exist in the output directory. This command accepts the following flags:
--force
causes the files to be generated even if files with the same name already exist, overwriting the files with new empty scaffolding.--regen
causes the scaffolding to be regenerated in existing files, leaving controller implementations in place. See themain
documentation for more details.--res
specifies the name of the resource and is compulsory.--pkg
specifies the name of the generated controller package, the behavior is as follows:--out
is not specified (default)--pkg
is not specified (default)- the package is generated as
main
. (This behavior matches themain
andbootstrap
commands.)
- the package is generated as
--pkg
is specifield (bar
)- the package is generated as
bar
.
- the package is generated as
--out
is specified (foo
)--pkg
is not specified (default)- the package is generated as
foo
.
- the package is generated as
--pkg
is specifield (bar
)- the package is generated as
bar
.
- the package is generated as
--app-pkg
sets the name of the generated Go package containing the controllers supporting code (contexts, media types, user types etc.), defaults toapp
.
Workaround when Subversion .svn directories are deleted by goagen executable
When generating new code with goagen, and using Subversion < 1.7, all hidden repository metadata directories with name .svn were also deleted. Because of that, repository directory becomes unusable. For Subversion version >= 1.7 repository metadata is stored only in one root .svn directory, as by Git, and in that case this issue is not occuring. As workaround, I am calling goagen from start.bat file (on Windows):
rmdir /Q /S gentemp
mkdir gentemp\app\.svn
mkdir gentemp\app\test\.svn
mkdir gentemp\client\.svn
mkdir gentemp\swagger\.svn
mkdir gentemp\tool\cli\.svn
xcopy /s app\.svn\*.* gentemp\app\.svn
xcopy /s app\test\.svn\*.* gentemp\app\test\.svn
xcopy /s client\.svn\*.* gentemp\client\.svn
xcopy /s swagger\.svn\*.* gentemp\swagger\.svn
xcopy /s tool\cli\.svn\*.* gentemp\tool\cli\.svn
goagen bootstrap -d myprojects/myapi/design
mkdir app\.svn
xcopy /s gentemp\app\.svn\*.* app\.svn
mkdir app\.svn\tmp\prop-base
mkdir app\.svn\tmp\props
mkdir app\.svn\tmp\text-base
mkdir app\test\.svn
xcopy /s gentemp\app\test\.svn\*.* app\test\.svn
mkdir app\test\.svn\tmp\prop-base
mkdir app\test\.svn\tmp\props
mkdir app\test\.svn\tmp\text-base
mkdir client\.svn
xcopy /s gentemp\client\.svn\*.* client\.svn
mkdir client\.svn\tmp\prop-base
mkdir client\.svn\tmp\props
mkdir client\.svn\tmp\text-base
mkdir swagger\.svn
xcopy /s gentemp\swagger\.svn\*.* swagger\.svn
mkdir swagger\.svn\tmp\prop-base
mkdir swagger\.svn\tmp\props
mkdir swagger\.svn\tmp\text-base
mkdir tool\cli\.svn
xcopy /s gentemp\tool\cli\.svn\*.* tool\cli\.svn
mkdir tool\cli\.svn\tmp\prop-base
mkdir tool\cli\.svn\tmp\props
mkdir tool\cli\.svn\tmp\text-base
rmdir /Q /S gentemp