Skip to content

Commit 08bf055

Browse files
committed
Add versioning support to swagger generator
1 parent 07a9adb commit 08bf055

10 files changed

+130
-91
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ lint:
6363
fi
6464

6565
cyclo:
66-
@if [ "`gocyclo -over 20 . | grep -v examples/cellar | tee /dev/stderr`" ]; then \
66+
@if [ "`gocyclo -over 20 . | grep -v _integration_tests | tee /dev/stderr`" ]; then \
6767
echo "^ - Cyclomatic complexity exceeds 20, refactor the code!" && echo && exit 1; \
6868
fi
6969

goagen/gen_app/generator_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ func MountWidgetController(service *goa.Service, ctrl WidgetController) {
461461
rctx.APIVersion = "{{.version}}"{{end}}
462462
return ctrl.Get(rctx)
463463
}
464-
mux.Handle("GET", "/:id", ctrl.MuxHandler("Get", "{{.version}}", h, nil))
464+
mux.Handle("GET", "/:id", ctrl.MuxHandler("Get", h, nil))
465465
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Widget"},{{if .version}} goa.KV{"version", "{{.version}}"},{{end}} goa.KV{"action", "Get"}, goa.KV{"route", "GET /:id"})
466466
}
467467
`
@@ -517,7 +517,7 @@ func MountWidgetController(service *goa.Service, ctrl WidgetController) {
517517
}
518518
return ctrl.Get(rctx)
519519
}
520-
mux.Handle("GET", "/:id", ctrl.MuxHandler("Get", "", h, unmarshalGetWidgetPayload))
520+
mux.Handle("GET", "/:id", ctrl.MuxHandler("Get", h, unmarshalGetWidgetPayload))
521521
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Widget"}, goa.KV{"action", "Get"}, goa.KV{"route", "GET /:id"})
522522
}
523523

goagen/gen_app/writers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ func Mount{{.Resource}}Controller(service *goa.Service, ctrl {{.Resource}}Contro
623623
}
624624
{{end}} return ctrl.{{.Name}}(rctx)
625625
}
626-
{{range .Routes}} mux.Handle("{{.Verb}}", "{{.FullPath $ver}}", ctrl.MuxHandler("{{$action.Name}}", "{{$ver.Version}}", h, {{if $action.Payload}}{{$action.Unmarshal}}{{else}}nil{{end}}))
626+
{{range .Routes}} mux.Handle("{{.Verb}}", "{{.FullPath $ver}}", ctrl.MuxHandler("{{$action.Name}}", h, {{if $action.Payload}}{{$action.Unmarshal}}{{else}}nil{{end}}))
627627
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "{{$res}}"},{{if not $ver.IsDefault}} goa.KV{"version", "{{$ver.Version}}"},{{end}} goa.KV{"action", "{{$action.Name}}"}, goa.KV{"route", "{{.Verb}} {{.FullPath $ver}}"})
628628
{{end}}{{end}}}
629629
`

goagen/gen_app/writers_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ func MountBottlesController(service *goa.Service, ctrl BottlesController) {
10751075
}
10761076
return ctrl.List(rctx)
10771077
}
1078-
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", "", h, nil))
1078+
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", h, nil))
10791079
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Bottles"}, goa.KV{"action", "List"}, goa.KV{"route", "GET /accounts/:accountID/bottles"})
10801080
}
10811081
`
@@ -1091,7 +1091,7 @@ func MountBottlesController(service *goa.Service, ctrl BottlesController) {
10911091
}
10921092
return ctrl.List(rctx)
10931093
}
1094-
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", "", h, nil))
1094+
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", h, nil))
10951095
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Bottles"}, goa.KV{"action", "List"}, goa.KV{"route", "GET /accounts/:accountID/bottles"})
10961096
}
10971097
`
@@ -1115,7 +1115,7 @@ type BottlesController interface {
11151115
}
11161116
return ctrl.List(rctx)
11171117
}
1118-
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", "", h, nil))
1118+
mux.Handle("GET", "/accounts/:accountID/bottles", ctrl.MuxHandler("List", h, nil))
11191119
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Bottles"}, goa.KV{"action", "List"}, goa.KV{"route", "GET /accounts/:accountID/bottles"})
11201120
h = func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
11211121
rctx, err := NewShowBottleContext(ctx)
@@ -1124,7 +1124,7 @@ type BottlesController interface {
11241124
}
11251125
return ctrl.Show(rctx)
11261126
}
1127-
mux.Handle("GET", "/accounts/:accountID/bottles/:id", ctrl.MuxHandler("Show", "", h, nil))
1127+
mux.Handle("GET", "/accounts/:accountID/bottles/:id", ctrl.MuxHandler("Show", h, nil))
11281128
goa.Info(goa.RootContext, "mount", goa.KV{"ctrl", "Bottles"}, goa.KV{"action", "Show"}, goa.KV{"route", "GET /accounts/:accountID/bottles/:id"})
11291129
}
11301130
`

goagen/gen_swagger/generator.go

+44-19
Original file line numberDiff line numberDiff line change
@@ -50,47 +50,72 @@ func (g *Generator) Generate(api *design.APIDefinition) (_ []string, err error)
5050
}
5151
}()
5252

53-
s, err := New(api)
54-
if err != nil {
55-
return
56-
}
57-
b, err := json.Marshal(s)
58-
if err != nil {
59-
return
60-
}
6153
swaggerDir := filepath.Join(codegen.OutputDir, "swagger")
6254
os.RemoveAll(swaggerDir)
6355
if err = os.MkdirAll(swaggerDir, 0755); err != nil {
64-
return
56+
return nil, err
6557
}
6658
genfiles = append(genfiles, swaggerDir)
67-
swaggerFile := filepath.Join(swaggerDir, "swagger.json")
68-
err = ioutil.WriteFile(swaggerFile, b, 0644)
59+
60+
err = api.IterateVersions(func(ver *design.APIVersionDefinition) error {
61+
s, err := New(api, ver)
62+
if err != nil {
63+
return err
64+
}
65+
b, err := json.Marshal(s)
66+
if err != nil {
67+
return err
68+
}
69+
verDir := swaggerDir
70+
if ver.Version != "" {
71+
verDir = filepath.Join(swaggerDir, ver.Version)
72+
if err = os.MkdirAll(verDir, 0755); err != nil {
73+
return err
74+
}
75+
genfiles = append(genfiles, verDir)
76+
}
77+
swaggerFile := filepath.Join(verDir, "swagger.json")
78+
err = ioutil.WriteFile(swaggerFile, b, 0644)
79+
if err != nil {
80+
return err
81+
}
82+
genfiles = append(genfiles, swaggerFile)
83+
return nil
84+
})
6985
if err != nil {
70-
return
86+
return nil, err
7187
}
72-
genfiles = append(genfiles, swaggerFile)
7388
controllerFile := filepath.Join(swaggerDir, "swagger.go")
7489
genfiles = append(genfiles, controllerFile)
7590
file, err := codegen.SourceFileFor(controllerFile)
7691
if err != nil {
77-
return
92+
return nil, err
7893
}
7994
imports := []*codegen.ImportSpec{
8095
codegen.SimpleImport("github.com/goadesign/goa"),
8196
}
8297
file.WriteHeader(fmt.Sprintf("%s Swagger Spec", api.Name), "swagger", imports)
83-
file.Write([]byte(swagger))
98+
var versions []*design.APIVersionDefinition
99+
api.IterateVersions(func(ver *design.APIVersionDefinition) error {
100+
versions = append(versions, ver)
101+
return nil
102+
})
103+
err = file.ExecuteTemplate("swagger", swaggerT, nil, map[string]interface{}{"Versions": versions})
104+
if err != nil {
105+
return nil, err
106+
}
84107
if err = file.FormatCode(); err != nil {
85-
return
108+
return nil, err
86109
}
87110

88111
return genfiles, nil
89112
}
90113

91-
const swagger = `
92-
// MountController mounts the swagger spec controller under "/swagger.json".
114+
const swaggerT = `
115+
// MountController mounts the swagger spec controllers (one per API version) under "/swagger.json".
93116
func MountController(service *goa.Service) {
94-
service.ServeFiles("/swagger.json", "swagger/swagger.json")
117+
{{range .Versions}} service{{if .Version}}.Version("{{.Version}}"){{end}}{{/*
118+
*/}}.ServeFiles("/swagger.json", "swagger/{{if .Version}}{{.Version}}/{{end}}swagger.json")
119+
{{end}}
95120
}
96121
`

goagen/gen_swagger/swagger.go

+19-16
Original file line numberDiff line numberDiff line change
@@ -278,15 +278,15 @@ type (
278278
)
279279

280280
// New creates a Swagger spec from an API definition.
281-
func New(api *design.APIDefinition) (*Swagger, error) {
281+
func New(api *design.APIDefinition, ver *design.APIVersionDefinition) (*Swagger, error) {
282282
if api == nil {
283283
return nil, nil
284284
}
285-
tags, err := tagsFromDefinition(api.Metadata)
285+
tags, err := tagsFromDefinition(ver.Metadata)
286286
if err != nil {
287287
return nil, err
288288
}
289-
params, err := paramsFromDefinition(api.BaseParams, api.BasePath)
289+
params, err := paramsFromDefinition(ver.BaseParams, ver.BasePath)
290290
if err != nil {
291291
return nil, err
292292
}
@@ -298,35 +298,35 @@ func New(api *design.APIDefinition) (*Swagger, error) {
298298
}
299299
}
300300
var consumes []string
301-
for _, c := range api.Consumes {
301+
for _, c := range ver.Consumes {
302302
consumes = append(consumes, c.MIMETypes...)
303303
}
304304
var produces []string
305-
for _, p := range api.Produces {
305+
for _, p := range ver.Produces {
306306
produces = append(produces, p.MIMETypes...)
307307
}
308308
s := &Swagger{
309309
Swagger: "2.0",
310310
Info: &Info{
311-
Title: api.Title,
312-
Description: api.Description,
313-
TermsOfService: api.TermsOfService,
314-
Contact: api.Contact,
315-
License: api.License,
316-
Version: "",
311+
Title: ver.Title,
312+
Description: ver.Description,
313+
TermsOfService: ver.TermsOfService,
314+
Contact: ver.Contact,
315+
License: ver.License,
316+
Version: ver.Version,
317317
},
318-
Host: api.Host,
319-
BasePath: api.BasePath,
318+
Host: ver.Host,
319+
BasePath: ver.BasePath,
320320
Paths: make(map[string]*Path),
321-
Schemes: api.Schemes,
321+
Schemes: ver.Schemes,
322322
Consumes: consumes,
323323
Produces: produces,
324324
Parameters: paramMap,
325325
Tags: tags,
326-
ExternalDocs: docsFromDefinition(api.Docs),
326+
ExternalDocs: docsFromDefinition(ver.Docs),
327327
}
328328

329-
err = api.IterateResponses(func(r *design.ResponseDefinition) error {
329+
err = ver.IterateResponses(func(r *design.ResponseDefinition) error {
330330
res, err := responseSpecFromDefinition(s, api, r)
331331
if err != nil {
332332
return err
@@ -341,6 +341,9 @@ func New(api *design.APIDefinition) (*Swagger, error) {
341341
return nil, err
342342
}
343343
err = api.IterateResources(func(res *design.ResourceDefinition) error {
344+
if !res.SupportsVersion(ver.Version) {
345+
return nil
346+
}
344347
return res.IterateActions(func(a *design.ActionDefinition) error {
345348
for _, route := range a.Routes {
346349
if err := buildPathFromDefinition(s, api, route); err != nil {

goagen/gen_swagger/swagger_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var _ = Describe("New", func() {
3737
JustBeforeEach(func() {
3838
err := dslengine.Run()
3939
Ω(err).ShouldNot(HaveOccurred())
40-
swagger, newErr = genswagger.New(Design)
40+
swagger, newErr = genswagger.New(Design, Design.APIVersionDefinition)
4141
})
4242

4343
Context("with a valid API definition", func() {

mux.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type (
3737

3838
// Muxer implements an adapter that given a request handler can produce a mux handler.
3939
Muxer interface {
40-
MuxHandler(name, version string, hdlr Handler, unm Unmarshaler) MuxHandler
40+
MuxHandler(name string, hdlr Handler, unm Unmarshaler) MuxHandler
4141
}
4242

4343
// RootMux is the default VersionMux and ServeMux implementation. It dispatches requests to the

0 commit comments

Comments
 (0)