diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 4c6c51a1dcb..7ee873ff3ee 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -65,8 +65,8 @@ }, { "ImportPath": "github.com/emicklei/go-restful", - "Comment": "v1.1.2-34-gcb26ade", - "Rev": "cb26adeb9644200cb4ec7b32be31e024696e8d00" + "Comment": "v1.1.2-38-gab99062", + "Rev": "ab990627e3546d3c6c8ab45c4d887a3d66b1b6ab" }, { "ImportPath": "github.com/fsouza/go-dockerclient", diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md b/Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md index a04408b7ffa..485540233ee 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md @@ -1,5 +1,11 @@ Change history of go-restful = +2014-11-12 +- (api add) ApiVersion(.) for documentation in Swagger UI + +2014-11-10 +- (api change) struct fields tagged with "description" show up in Swagger UI + 2014-10-31 - (api change) ReturnsError -> Returns - (api add) RouteBuilder.Do(aBuilder) for DRY use of RouteBuilder diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/CHANGES.md b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/CHANGES.md index cae91f39dcb..9980f2f1dff 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/CHANGES.md +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/CHANGES.md @@ -1,5 +1,13 @@ Change history of swagger = + +2014-11-14 +- operation parameters are now sorted using ordering path,query,form,header,body + +2014-11-12 +- respect omitempty tag value for embedded structs +- expose ApiVersion of WebService to Swagger ApiDeclaration + 2014-05-29 - (api add) Ability to define custom http.Handler to serve swagger-ui static files diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/README.md b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/README.md index d4f4fc421e3..2efe8f3a054 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/README.md +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/README.md @@ -15,7 +15,6 @@ Now, you can install the Swagger WebService for serving the Swagger specificatio config := swagger.Config{ WebServices: restful.RegisteredWebServices(), - WebServicesUrl: "http://localhost:8080", ApiPath: "/apidocs.json", SwaggerPath: "/apidocs/", SwaggerFilePath: "/Users/emicklei/Projects/swagger-ui/dist"} @@ -26,3 +25,4 @@ Notes -- - Use RouteBuilder.Operation(..) to set the Nickname field of the API spec - The WebServices field of swagger.Config can be used to control which service you want to expose and document ; you can have multiple configs and therefore multiple endpoints. +- Use tag "description" to annotate a struct field with a description to show in the UI \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder.go index 71a69e4b615..68524ecfc53 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder.go @@ -43,6 +43,9 @@ func (b modelBuilder) addModel(st reflect.Type, nameOverride string) { for i := 0; i < st.NumField(); i++ { field := st.Field(i) jsonName, prop := b.buildProperty(field, &sm, modelName) + if descTag := field.Tag.Get("description"); descTag != "" { + prop.Description = descTag + } // add if not ommitted if len(jsonName) != 0 { // update Required @@ -80,7 +83,6 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod if jsonTag := field.Tag.Get("json"); jsonTag != "" { s := strings.Split(jsonTag, ",") if len(s) > 1 && s[1] == "string" { - prop.Description = "(" + fieldType.String() + " as string)" fieldType = reflect.TypeOf("") } } @@ -139,7 +141,17 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam subModel := sub.Models[subKey] for k, v := range subModel.Properties { model.Properties[k] = v - model.Required = append(model.Required, k) + // if subModel says this property is required then include it + required := false + for _, each := range subModel.Required { + if k == each { + required = true + break + } + } + if required { + model.Required = append(model.Required, k) + } } // empty name signals skip property return "", prop diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go index 8dc8073de61..dd966bdfe65 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder_test.go @@ -143,7 +143,7 @@ func TestS3(t *testing.T) { type sample struct { id string `swagger:"required"` // TODO items []item - rootItem item `json:"root"` + rootItem item `json:"root" description:"root desc"` } type item struct { @@ -184,7 +184,8 @@ func TestSampleToModelAsJson(t *testing.T) { ] }, "root": { - "type": "swagger.item" + "type": "swagger.item", + "description": "root desc" } } } @@ -212,8 +213,7 @@ func TestJsonTags(t *testing.T) { "type": "string" }, "C": { - "type": "string", - "description": "(int as string)" + "type": "string" }, "D": { "type": "integer", @@ -536,7 +536,8 @@ func TestRecursiveStructure(t *testing.T) { type A1 struct { B struct { - Id int + Id int + Comment string `json:"comment,omitempty"` } } @@ -563,6 +564,9 @@ func TestEmbeddedStructA1(t *testing.T) { "Id": { "type": "integer", "format": "int32" + }, + "comment": { + "type": "string" } } } @@ -573,7 +577,9 @@ type A2 struct { C } type C struct { - Id int `json:"B"` + Id int `json:"B"` + Comment string `json:"comment,omitempty"` + Secure bool `json:"secure"` } // go test -v -test.run TestEmbeddedStructA2 ...swagger @@ -582,12 +588,19 @@ func TestEmbeddedStructA2(t *testing.T) { "swagger.A2": { "id": "swagger.A2", "required": [ - "B" + "B", + "secure" ], "properties": { "B": { "type": "integer", "format": "int32" + }, + "comment": { + "type": "string" + }, + "secure": { + "type": "boolean" } } } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter.go new file mode 100644 index 00000000000..813007b7d7a --- /dev/null +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter.go @@ -0,0 +1,29 @@ +package swagger + +// Copyright 2014 Ernest Micklei. All rights reserved. +// Use of this source code is governed by a license +// that can be found in the LICENSE file. + +type ParameterSorter []Parameter + +func (s ParameterSorter) Len() int { + return len(s) +} +func (s ParameterSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +var typeToSortKey = map[string]string{ + "path": "A", + "query": "B", + "form": "C", + "header": "D", + "body": "E", +} + +func (s ParameterSorter) Less(i, j int) bool { + // use ordering path,query,form,header,body + pi := s[i] + pj := s[j] + return typeToSortKey[pi.ParamType]+pi.Name < typeToSortKey[pj.ParamType]+pj.Name +} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter_test.go new file mode 100644 index 00000000000..ef6d9ebd195 --- /dev/null +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/param_sorter_test.go @@ -0,0 +1,52 @@ +package swagger + +import ( + "bytes" + "sort" + "testing" +) + +func TestSortParameters(t *testing.T) { + unsorted := []Parameter{ + Parameter{ + Name: "form2", + ParamType: "form", + }, + Parameter{ + Name: "header1", + ParamType: "header", + }, + Parameter{ + Name: "path2", + ParamType: "path", + }, + Parameter{ + Name: "body", + ParamType: "body", + }, + Parameter{ + Name: "path1", + ParamType: "path", + }, + Parameter{ + Name: "form1", + ParamType: "form", + }, + Parameter{ + Name: "query2", + ParamType: "query", + }, + Parameter{ + Name: "query1", + ParamType: "query", + }, + } + sort.Sort(ParameterSorter(unsorted)) + var b bytes.Buffer + for _, p := range unsorted { + b.WriteString(p.Name + ".") + } + if "path1.path2.query1.query2.form1.form2.header1.body." != b.String() { + t.Fatal("sorting has changed:" + b.String()) + } +} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go index 12f4e14bd3e..81e72f61ac3 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_test.go @@ -21,6 +21,7 @@ func TestServiceToApi(t *testing.T) { ws.Consumes(restful.MIME_JSON) ws.Produces(restful.MIME_XML) ws.Route(ws.GET("/all").To(dummy).Writes(sample{})) + ws.ApiVersion("1.2.3") cfg := Config{ WebServicesUrl: "http://here.com", ApiPath: "/apipath", diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go index c20de2e2f27..04da0a12406 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go @@ -155,7 +155,8 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix SwaggerVersion: swaggerVersion, BasePath: sws.config.WebServicesUrl, ResourcePath: ws.RootPath(), - Models: map[string]Model{}} + Models: map[string]Model{}, + ApiVersion: ws.Version()} // collect any path parameters rootParams := []Parameter{} @@ -192,6 +193,9 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix for _, param := range route.ParameterDocs { operation.Parameters = append(operation.Parameters, asSwaggerParameter(param.Data())) } + // sort parameters + sort.Sort(ParameterSorter(operation.Parameters)) + sws.addModelsFromRouteTo(&operation, route, &decl) api.Operations = append(api.Operations, operation) } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service.go index ce261b541e8..323676f28a1 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service.go @@ -18,6 +18,7 @@ type WebService struct { pathParameters []*Parameter filters []FilterFunction documentation string + apiVersion string } // compiledPathExpression ensures that the path is compiled into a RegEx for those routers that need it. @@ -35,6 +36,15 @@ func (w *WebService) compiledPathExpression() *pathExpression { return w.pathExpr } +// ApiVersion sets the API version for documentation purposes. +func (w *WebService) ApiVersion(apiVersion string) *WebService { + w.apiVersion = apiVersion + return w +} + +// Version returns the API version for documentation purposes. +func (w WebService) Version() string { return w.apiVersion } + // Path specifies the root URL template path of the WebService. // All Routes will be relative to this path. func (w *WebService) Path(root string) *WebService {