diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 34bde9b42b4..98922b6fad6 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -157,8 +157,8 @@ }, { "ImportPath": "github.com/emicklei/go-restful", - "Comment": "v1.1.3-54-gbdfb7d4", - "Rev": "bdfb7d41639a84ea7c36df648e5865cd9fbf21e2" + "Comment": "v1.1.3-45-gd487287", + "Rev": "d4872876992d385f0e69b007f154e5633bdb40af" }, { "ImportPath": "github.com/evanphx/json-patch", diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go deleted file mode 100644 index 7746b5b0705..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-swagger.go +++ /dev/null @@ -1,61 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful/swagger" -) - -type Book struct { - Title string - Author string -} - -func main() { - ws := new(restful.WebService) - ws.Path("/books") - ws.Consumes(restful.MIME_JSON, restful.MIME_XML) - ws.Produces(restful.MIME_JSON, restful.MIME_XML) - restful.Add(ws) - - ws.Route(ws.GET("/{medium}").To(noop). - Doc("Search all books"). - Param(ws.PathParameter("medium", "digital or paperback").DataType("string")). - Param(ws.QueryParameter("language", "en,nl,de").DataType("string")). - Param(ws.HeaderParameter("If-Modified-Since", "last known timestamp").DataType("datetime")). - Do(returns200, returns500)) - - ws.Route(ws.PUT("/{medium}").To(noop). - Doc("Add a new book"). - Param(ws.PathParameter("medium", "digital or paperback").DataType("string")). - Reads(Book{})) - - // You can install the Swagger Service which provides a nice Web UI on your REST API - // You need to download the Swagger HTML5 assets and change the FilePath location in the config below. - // Open http://localhost:8080/apidocs and enter http://localhost:8080/apidocs.json in the api input field. - config := swagger.Config{ - WebServices: restful.DefaultContainer.RegisteredWebServices(), // you control what services are visible - WebServicesUrl: "http://localhost:8080", - ApiPath: "/apidocs.json", - - // Optionally, specifiy where the UI is located - SwaggerPath: "/apidocs/", - SwaggerFilePath: "/Users/emicklei/xProjects/swagger-ui/dist"} - swagger.RegisterSwaggerService(config, restful.DefaultContainer) - - log.Printf("start listening on localhost:8080") - server := &http.Server{Addr: ":8080", Handler: restful.DefaultContainer} - log.Fatal(server.ListenAndServe()) -} - -func noop(req *restful.Request, resp *restful.Response) {} - -func returns200(b *restful.RouteBuilder) { - b.Returns(http.StatusOK, "OK", Book{}) -} - -func returns500(b *restful.RouteBuilder) { - b.Returns(http.StatusInternalServerError, "Bummer, something went wrong", nil) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go index 05a9987600f..7f38a0a6478 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go @@ -35,7 +35,6 @@ type ParameterData struct { Required bool AllowableValues map[string]string AllowMultiple bool - DefaultValue string } // Data returns the state of the Parameter @@ -71,32 +70,26 @@ func (p *Parameter) beForm() *Parameter { return p } -// Required sets the required field and returns the receiver +// Required sets the required field and return the receiver func (p *Parameter) Required(required bool) *Parameter { p.data.Required = required return p } -// AllowMultiple sets the allowMultiple field and returns the receiver +// AllowMultiple sets the allowMultiple field and return the receiver func (p *Parameter) AllowMultiple(multiple bool) *Parameter { p.data.AllowMultiple = multiple return p } -// AllowableValues sets the allowableValues field and returns the receiver +// AllowableValues sets the allowableValues field and return the receiver func (p *Parameter) AllowableValues(values map[string]string) *Parameter { p.data.AllowableValues = values return p } -// DataType sets the dataType field and returns the receiver +// DataType sets the dataType field and return the receiver func (p *Parameter) DataType(typeName string) *Parameter { p.data.DataType = typeName return p } - -// DefaultValue sets the default value field and returnw the receiver -func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter { - p.data.DefaultValue = stringRepresentation - return p -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go index 6a104aff8ed..78b3be88fd3 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/route_test.go @@ -4,14 +4,6 @@ import ( "testing" ) -// accept should match produces -func TestMatchesAcceptPlainTextWhenProducePlainTextAsLast(t *testing.T) { - r := Route{Produces: []string{"application/json", "text/plain"}} - if !r.matchesAccept("text/plain") { - t.Errorf("accept should match text/plain") - } -} - // accept should match produces func TestMatchesAcceptStar(t *testing.T) { r := Route{Produces: []string{"application/xml"}} 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 c9b49044565..6a392cf2500 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,10 +1,5 @@ Change history of swagger = -2015-05-25 -- (api break) changed the type of Properties in Model -- (api break) changed the type of Models in ApiDeclaration -- (api break) changed the parameter type of PostBuildDeclarationMapFunc - 2015-04-09 - add ModelBuildable interface for customization of Model 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 9376fc10af6..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 @@ -23,6 +23,6 @@ Now, you can install the Swagger WebService for serving the Swagger specificatio Notes -- -- The Nickname of an Operation is automatically set by finding the name of the function. You can override it using RouteBuilder.Operation(..) +- 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/api_declaration_list.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/api_declaration_list.go deleted file mode 100644 index 9f4c3690acb..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/api_declaration_list.go +++ /dev/null @@ -1,64 +0,0 @@ -package swagger - -// Copyright 2015 Ernest Micklei. All rights reserved. -// Use of this source code is governed by a license -// that can be found in the LICENSE file. - -import ( - "bytes" - "encoding/json" -) - -// ApiDeclarationList maintains an ordered list of ApiDeclaration. -type ApiDeclarationList struct { - List []ApiDeclaration -} - -// At returns the ApiDeclaration by its path unless absent, then ok is false -func (l *ApiDeclarationList) At(path string) (a ApiDeclaration, ok bool) { - for _, each := range l.List { - if each.ResourcePath == path { - return each, true - } - } - return a, false -} - -// Put adds or replaces a ApiDeclaration with this name -func (l *ApiDeclarationList) Put(path string, a ApiDeclaration) { - // maybe replace existing - for i, each := range l.List { - if each.ResourcePath == path { - // replace - l.List[i] = a - return - } - } - // add - l.List = append(l.List, a) -} - -// Do enumerates all the properties, each with its assigned name -func (l *ApiDeclarationList) Do(block func(path string, decl ApiDeclaration)) { - for _, each := range l.List { - block(each.ResourcePath, each) - } -} - -// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty -func (l ApiDeclarationList) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - buf.WriteString("{\n") - for i, each := range l.List { - buf.WriteString("\"") - buf.WriteString(each.ResourcePath) - buf.WriteString("\": ") - encoder.Encode(each) - if i < len(l.List)-1 { - buf.WriteString(",\n") - } - } - buf.WriteString("}") - return buf.Bytes(), nil -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/config.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/config.go index b272b7bface..c08586bb5ce 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/config.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/config.go @@ -7,7 +7,7 @@ import ( ) // PostBuildDeclarationMapFunc can be used to modify the api declaration map. -type PostBuildDeclarationMapFunc func(apiDeclarationMap *ApiDeclarationList) +type PostBuildDeclarationMapFunc func(apiDeclarationMap map[string]ApiDeclaration) type Config struct { // url where the services are available, e.g. http://localhost:8080 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 2ecc49a6c16..631ad53a334 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 @@ -13,7 +13,7 @@ type ModelBuildable interface { } type modelBuilder struct { - Models *ModelList + Models map[string]Model } // addModelFrom creates and adds a Model to the builder and detects and calls @@ -23,7 +23,7 @@ func (b modelBuilder) addModelFrom(sample interface{}) { // allow customizations if buildable, ok := sample.(ModelBuildable); ok { modelOrNil = buildable.PostBuildModel(modelOrNil) - b.Models.Put(modelOrNil.Id, *modelOrNil) + b.Models[modelOrNil.Id] = *modelOrNil } } } @@ -38,16 +38,16 @@ func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model { return nil } // see if we already have visited this model - if _, ok := b.Models.At(modelName); ok { + if _, ok := b.Models[modelName]; ok { return nil } sm := Model{ Id: modelName, Required: []string{}, - Properties: ModelPropertyList{}} + Properties: map[string]ModelProperty{}} // reference the model before further initializing (enables recursive structs) - b.Models.Put(modelName, sm) + b.Models[modelName] = sm // check for slice or array if st.Kind() == reflect.Slice || st.Kind() == reflect.Array { @@ -70,11 +70,11 @@ func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model { if b.isPropertyRequired(field) { sm.Required = append(sm.Required, jsonName) } - sm.Properties.Put(jsonName, prop) + sm.Properties[jsonName] = prop } } // update model builder with completed model - b.Models.Put(modelName, sm) + b.Models[modelName] = sm return &sm } @@ -179,13 +179,13 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) { // embedded struct - sub := modelBuilder{new(ModelList)} + sub := modelBuilder{map[string]Model{}} sub.addModel(fieldType, "") subKey := sub.keyFrom(fieldType) // merge properties from sub - subModel, _ := sub.Models.At(subKey) - subModel.Properties.Do(func(k string, v ModelProperty) { - model.Properties.Put(k, v) + subModel := sub.Models[subKey] + for k, v := range subModel.Properties { + model.Properties[k] = v // if subModel says this property is required then include it required := false for _, each := range subModel.Required { @@ -197,15 +197,15 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam if required { model.Required = append(model.Required, k) } - }) + } // add all new referenced models - sub.Models.Do(func(key string, sub Model) { + for key, sub := range sub.Models { if key != subKey { - if _, ok := b.Models.At(key); !ok { - b.Models.Put(key, sub) + if _, ok := b.Models[key]; !ok { + b.Models[key] = sub } } - }) + } // empty name signals skip property return "", prop } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list.go deleted file mode 100644 index 9bb6cb67850..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list.go +++ /dev/null @@ -1,86 +0,0 @@ -package swagger - -// Copyright 2015 Ernest Micklei. All rights reserved. -// Use of this source code is governed by a license -// that can be found in the LICENSE file. - -import ( - "bytes" - "encoding/json" -) - -// NamedModel associates a name with a Model (not using its Id) -type NamedModel struct { - Name string - Model Model -} - -// ModelList encapsulates a list of NamedModel (association) -type ModelList struct { - List []NamedModel -} - -// Put adds or replaces a Model by its name -func (l *ModelList) Put(name string, model Model) { - for i, each := range l.List { - if each.Name == name { - // replace - l.List[i] = NamedModel{name, model} - return - } - } - // add - l.List = append(l.List, NamedModel{name, model}) -} - -// At returns a Model by its name, ok is false if absent -func (l *ModelList) At(name string) (m Model, ok bool) { - for _, each := range l.List { - if each.Name == name { - return each.Model, true - } - } - return m, false -} - -// Do enumerates all the models, each with its assigned name -func (l *ModelList) Do(block func(name string, value Model)) { - for _, each := range l.List { - block(each.Name, each.Model) - } -} - -// MarshalJSON writes the ModelList as if it was a map[string]Model -func (l ModelList) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - buf.WriteString("{\n") - for i, each := range l.List { - buf.WriteString("\"") - buf.WriteString(each.Name) - buf.WriteString("\": ") - encoder.Encode(each.Model) - if i < len(l.List)-1 { - buf.WriteString(",\n") - } - } - buf.WriteString("}") - return buf.Bytes(), nil -} - -// UnmarshalJSON reads back a ModelList. This is an expensive operation. -func (l *ModelList) UnmarshalJSON(data []byte) error { - raw := map[string]interface{}{} - json.NewDecoder(bytes.NewReader(data)).Decode(&raw) - for k, v := range raw { - // produces JSON bytes for each value - data, err := json.Marshal(v) - if err != nil { - return err - } - var m Model - json.NewDecoder(bytes.NewReader(data)).Decode(&m) - l.Put(k, m) - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go deleted file mode 100644 index 9a9ab919b48..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_list_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package swagger - -import ( - "encoding/json" - "testing" -) - -func TestModelList(t *testing.T) { - m := Model{} - m.Id = "m" - l := ModelList{} - l.Put("m", m) - k, ok := l.At("m") - if !ok { - t.Error("want model back") - } - if got, want := k.Id, "m"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelList_Marshal(t *testing.T) { - l := ModelList{} - m := Model{Id: "myid"} - l.Put("myid", m) - data, err := json.Marshal(l) - if err != nil { - t.Error(err) - } - if got, want := string(data), `{"myid":{"id":"myid","properties":{}}}`; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelList_Unmarshal(t *testing.T) { - data := `{"myid":{"id":"myid","properties":{}}}` - l := ModelList{} - if err := json.Unmarshal([]byte(data), &l); err != nil { - t.Error(err) - } - m, ok := l.At("myid") - if !ok { - t.Error("expected myid") - } - if got, want := m.Id, "myid"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list.go deleted file mode 100644 index 3babb194489..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list.go +++ /dev/null @@ -1,87 +0,0 @@ -package swagger - -// Copyright 2015 Ernest Micklei. All rights reserved. -// Use of this source code is governed by a license -// that can be found in the LICENSE file. - -import ( - "bytes" - "encoding/json" -) - -// NamedModelProperty associates a name to a ModelProperty -type NamedModelProperty struct { - Name string - Property ModelProperty -} - -// ModelPropertyList encapsulates a list of NamedModelProperty (association) -type ModelPropertyList struct { - List []NamedModelProperty -} - -// At returns the ModelPropety by its name unless absent, then ok is false -func (l *ModelPropertyList) At(name string) (p ModelProperty, ok bool) { - for _, each := range l.List { - if each.Name == name { - return each.Property, true - } - } - return p, false -} - -// Put adds or replaces a ModelProperty with this name -func (l *ModelPropertyList) Put(name string, prop ModelProperty) { - // maybe replace existing - for i, each := range l.List { - if each.Name == name { - // replace - l.List[i] = NamedModelProperty{Name: name, Property: prop} - return - } - } - // add - l.List = append(l.List, NamedModelProperty{Name: name, Property: prop}) -} - -// Do enumerates all the properties, each with its assigned name -func (l *ModelPropertyList) Do(block func(name string, value ModelProperty)) { - for _, each := range l.List { - block(each.Name, each.Property) - } -} - -// MarshalJSON writes the ModelPropertyList as if it was a map[string]ModelProperty -func (l ModelPropertyList) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - buf.WriteString("{\n") - for i, each := range l.List { - buf.WriteString("\"") - buf.WriteString(each.Name) - buf.WriteString("\": ") - encoder.Encode(each.Property) - if i < len(l.List)-1 { - buf.WriteString(",\n") - } - } - buf.WriteString("}") - return buf.Bytes(), nil -} - -// UnmarshalJSON reads back a ModelPropertyList. This is an expensive operation. -func (l *ModelPropertyList) UnmarshalJSON(data []byte) error { - raw := map[string]interface{}{} - json.NewDecoder(bytes.NewReader(data)).Decode(&raw) - for k, v := range raw { - // produces JSON bytes for each value - data, err := json.Marshal(v) - if err != nil { - return err - } - var m ModelProperty - json.NewDecoder(bytes.NewReader(data)).Decode(&m) - l.Put(k, m) - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go deleted file mode 100644 index 2833ad8fdaa..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_list_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package swagger - -import ( - "encoding/json" - "testing" -) - -func TestModelPropertyList(t *testing.T) { - l := ModelPropertyList{} - p := ModelProperty{Description: "d"} - l.Put("p", p) - q, ok := l.At("p") - if !ok { - t.Error("expected p") - } - if got, want := q.Description, "d"; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelPropertyList_Marshal(t *testing.T) { - l := ModelPropertyList{} - p := ModelProperty{Description: "d"} - l.Put("p", p) - data, err := json.Marshal(l) - if err != nil { - t.Error(err) - } - if got, want := string(data), `{"p":{"description":"d"}}`; got != want { - t.Errorf("got %v want %v", got, want) - } -} - -func TestModelPropertyList_Unmarshal(t *testing.T) { - data := `{"p":{"description":"d"}}` - l := ModelPropertyList{} - if err := json.Unmarshal([]byte(data), &l); err != nil { - t.Error(err) - } - m, ok := l.At("p") - if !ok { - t.Error("expected p") - } - if got, want := m.Description, "d"; got != want { - t.Errorf("got %v want %v", got, want) - } -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map.go index b33ccfbeb9e..f57163136ad 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/ordered_route_map.go @@ -1,9 +1,5 @@ package swagger -// Copyright 2015 Ernest Micklei. All rights reserved. -// Use of this source code is governed by a license -// that can be found in the LICENSE file. - import "github.com/emicklei/go-restful" type orderedRouteMap struct { 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/postbuild_model_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go index 3e20d2f5b9d..200b061486d 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/postbuild_model_test.go @@ -14,12 +14,12 @@ func (b Boat) PostBuildModel(m *Model) *Model { // add model property (just to test is can be added; is this a real usecase?) extraType := "string" - m.Properties.Put("extra", ModelProperty{ + m.Properties["extra"] = ModelProperty{ Description: "extra description", DataTypeFields: DataTypeFields{ Type: &extraType, }, - }) + } return m } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/resource_sorter.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/resource_sorter.go new file mode 100644 index 00000000000..e842b4c290d --- /dev/null +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/resource_sorter.go @@ -0,0 +1,19 @@ +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 ResourceSorter []Resource + +func (s ResourceSorter) Len() int { + return len(s) +} + +func (s ResourceSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s ResourceSorter) Less(i, j int) bool { + return s[i].Path < s[j].Path +} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger.go index 288aec67ef6..4aad3eebf05 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger.go @@ -114,15 +114,15 @@ type TokenEndpoint struct { // 5.2 API Declaration type ApiDeclaration struct { - SwaggerVersion string `json:"swaggerVersion"` - ApiVersion string `json:"apiVersion"` - BasePath string `json:"basePath"` - ResourcePath string `json:"resourcePath"` // must start with / - Apis []Api `json:"apis,omitempty"` - Models ModelList `json:"models,omitempty"` - Produces []string `json:"produces,omitempty"` - Consumes []string `json:"consumes,omitempty"` - Authorizations []Authorization `json:"authorizations,omitempty"` + SwaggerVersion string `json:"swaggerVersion"` + ApiVersion string `json:"apiVersion"` + BasePath string `json:"basePath"` + ResourcePath string `json:"resourcePath"` // must start with / + Apis []Api `json:"apis,omitempty"` + Models map[string]Model `json:"models,omitempty"` + Produces []string `json:"produces,omitempty"` + Consumes []string `json:"consumes,omitempty"` + Authorizations []Authorization `json:"authorizations,omitempty"` } // 5.2.2 API Object @@ -166,12 +166,12 @@ type ResponseMessage struct { // 5.2.6, 5.2.7 Models Object type Model struct { - Id string `json:"id"` - Description string `json:"description,omitempty"` - Required []string `json:"required,omitempty"` - Properties ModelPropertyList `json:"properties"` - SubTypes []string `json:"subTypes,omitempty"` - Discriminator string `json:"discriminator,omitempty"` + Id string `json:"id"` + Description string `json:"description,omitempty"` + Required []string `json:"required,omitempty"` + Properties map[string]ModelProperty `json:"properties"` + SubTypes []string `json:"subTypes,omitempty"` + Discriminator string `json:"discriminator,omitempty"` } // 5.2.8 Properties Object 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 cf38a760e5a..e810f2f35ef 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 @@ -26,7 +26,7 @@ func TestServiceToApi(t *testing.T) { WebServicesUrl: "http://here.com", ApiPath: "/apipath", WebServices: []*restful.WebService{ws}, - PostBuildHandler: func(in *ApiDeclarationList) {}, + PostBuildHandler: func(in map[string]ApiDeclaration) {}, } sws := newSwaggerService(cfg) decl := sws.composeDeclaration(ws, "/tests") @@ -73,7 +73,7 @@ func TestComposeResponseMessages(t *testing.T) { responseErrors[400] = restful.ResponseError{Code: 400, Message: "Bad Request", Model: TestItem{}} route := restful.Route{ResponseErrors: responseErrors} decl := new(ApiDeclaration) - decl.Models = ModelList{} + decl.Models = map[string]Model{} msgs := composeResponseMessages(route, decl) if msgs[0].ResponseModel != "swagger.TestItem" { t.Errorf("got %s want swagger.TestItem", msgs[0].ResponseModel) @@ -86,7 +86,7 @@ func TestComposeResponseMessageArray(t *testing.T) { responseErrors[400] = restful.ResponseError{Code: 400, Message: "Bad Request", Model: []TestItem{}} route := restful.Route{ResponseErrors: responseErrors} decl := new(ApiDeclaration) - decl.Models = ModelList{} + decl.Models = map[string]Model{} msgs := composeResponseMessages(route, decl) if msgs[0].ResponseModel != "array[swagger.TestItem]" { t.Errorf("got %s want swagger.TestItem", msgs[0].ResponseModel) @@ -95,23 +95,23 @@ func TestComposeResponseMessageArray(t *testing.T) { func TestIssue78(t *testing.T) { sws := newSwaggerService(Config{}) - models := new(ModelList) + models := map[string]Model{} sws.addModelFromSampleTo(&Operation{}, true, Response{Items: &[]TestItem{}}, models) - model, ok := models.At("swagger.Response") + model, ok := models["swagger.Response"] if !ok { t.Fatal("missing response model") } if "swagger.Response" != model.Id { t.Fatal("wrong model id:" + model.Id) } - code, ok := model.Properties.At("Code") + code, ok := model.Properties["Code"] if !ok { t.Fatal("missing code") } if "integer" != *code.Type { t.Fatal("wrong code type:" + *code.Type) } - items, ok := model.Properties.At("Items") + items, ok := model.Properties["Items"] if !ok { t.Fatal("missing items") } 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 885f9dc7316..9ac56c2de4e 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 @@ -15,13 +15,13 @@ import ( type SwaggerService struct { config Config - apiDeclarationMap *ApiDeclarationList + apiDeclarationMap map[string]ApiDeclaration } func newSwaggerService(config Config) *SwaggerService { return &SwaggerService{ config: config, - apiDeclarationMap: new(ApiDeclarationList)} + apiDeclarationMap: map[string]ApiDeclaration{}} } // LogInfo is the function that is called when this package needs to log. It defaults to log.Printf @@ -66,13 +66,13 @@ func RegisterSwaggerService(config Config, wsContainer *restful.Container) { // use routes for _, route := range each.Routes() { entry := staticPathFromRoute(route) - _, exists := sws.apiDeclarationMap.At(entry) + _, exists := sws.apiDeclarationMap[entry] if !exists { - sws.apiDeclarationMap.Put(entry, sws.composeDeclaration(each, entry)) + sws.apiDeclarationMap[entry] = sws.composeDeclaration(each, entry) } } } else { // use root path - sws.apiDeclarationMap.Put(each.RootPath(), sws.composeDeclaration(each, each.RootPath())) + sws.apiDeclarationMap[each.RootPath()] = sws.composeDeclaration(each, each.RootPath()) } } } @@ -139,22 +139,19 @@ func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.Fil func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Response) { listing := ResourceListing{SwaggerVersion: swaggerVersion, ApiVersion: sws.config.ApiVersion} - sws.apiDeclarationMap.Do(func(k string, v ApiDeclaration) { + for k, v := range sws.apiDeclarationMap { ref := Resource{Path: k} if len(v.Apis) > 0 { // use description of first (could still be empty) ref.Description = v.Apis[0].Description } listing.Apis = append(listing.Apis, ref) - }) + } + sort.Sort(ResourceSorter(listing.Apis)) resp.WriteAsJson(listing) } func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Response) { - decl, ok := sws.apiDeclarationMap.At(composeRootPath(req)) - if !ok { - resp.WriteErrorString(http.StatusNotFound, "ApiDeclaration not found") - return - } + decl := sws.apiDeclarationMap[composeRootPath(req)] // unless WebServicesUrl is given if len(sws.config.WebServicesUrl) == 0 { // update base path from the actual request @@ -183,7 +180,7 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix SwaggerVersion: swaggerVersion, BasePath: sws.config.WebServicesUrl, ResourcePath: ws.RootPath(), - Models: ModelList{}, + Models: map[string]Model{}, ApiVersion: ws.Version()} // collect any path parameters @@ -221,6 +218,8 @@ 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) @@ -254,7 +253,7 @@ func composeResponseMessages(route restful.Route, decl *ApiDeclaration) (message if isCollection { modelName = "array[" + modelName + "]" } - modelBuilder{&decl.Models}.addModel(st, "") + modelBuilder{decl.Models}.addModel(st, "") // reference the model message.ResponseModel = modelName } @@ -266,10 +265,10 @@ func composeResponseMessages(route restful.Route, decl *ApiDeclaration) (message // addModelsFromRoute takes any read or write sample from the Route and creates a Swagger model from it. func (sws SwaggerService) addModelsFromRouteTo(operation *Operation, route restful.Route, decl *ApiDeclaration) { if route.ReadSample != nil { - sws.addModelFromSampleTo(operation, false, route.ReadSample, &decl.Models) + sws.addModelFromSampleTo(operation, false, route.ReadSample, decl.Models) } if route.WriteSample != nil { - sws.addModelFromSampleTo(operation, true, route.WriteSample, &decl.Models) + sws.addModelFromSampleTo(operation, true, route.WriteSample, decl.Models) } } @@ -290,7 +289,7 @@ func detectCollectionType(st reflect.Type) (bool, reflect.Type) { } // addModelFromSample creates and adds (or overwrites) a Model from a sample resource -func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse bool, sample interface{}, models *ModelList) { +func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse bool, sample interface{}, models map[string]Model) { st := reflect.TypeOf(sample) isCollection, st := detectCollectionType(st) modelName := modelBuilder{}.keyFrom(st) @@ -306,9 +305,8 @@ func (sws SwaggerService) addModelFromSampleTo(operation *Operation, isResponse func asSwaggerParameter(param restful.ParameterData) Parameter { return Parameter{ DataTypeFields: DataTypeFields{ - Type: ¶m.DataType, - Format: asFormat(param.DataType), - DefaultValue: Special(param.DefaultValue), + Type: ¶m.DataType, + Format: asFormat(param.DataType), }, Name: param.Name, Description: param.Description, diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go index 6799174db4f..b0544481e1b 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/utils_test.go @@ -15,8 +15,8 @@ func testJsonFromStruct(t *testing.T, sample interface{}, expectedJson string) b return compareJson(t, string(data), expectedJson) } -func modelsFromStruct(sample interface{}) *ModelList { - models := new(ModelList) +func modelsFromStruct(sample interface{}) map[string]Model { + models := map[string]Model{} builder := modelBuilder{models} builder.addModelFrom(sample) return models @@ -28,12 +28,12 @@ func compareJson(t *testing.T, actualJsonAsString string, expectedJsonAsString s var expectedMap map[string]interface{} json.Unmarshal([]byte(expectedJsonAsString), &expectedMap) if !reflect.DeepEqual(actualMap, expectedMap) { - t.Log("---- expected -----") - t.Log(withLineNumbers(expectedJsonAsString)) - t.Log("---- actual -----") - t.Log(withLineNumbers(actualJsonAsString)) - t.Log("---- raw -----") - t.Log(actualJsonAsString) + fmt.Println("---- expected -----") + fmt.Println(withLineNumbers(expectedJsonAsString)) + fmt.Println("---- actual -----") + fmt.Println(withLineNumbers(actualJsonAsString)) + fmt.Println("---- raw -----") + fmt.Println(actualJsonAsString) t.Error("there are differences") return false } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go deleted file mode 100644 index 60c1e9fc09d..00000000000 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/tracer_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package restful - -import "testing" - -// Use like this: -// -// TraceLogger(testLogger{t}) -type testLogger struct { - t *testing.T -} - -func (l testLogger) Print(v ...interface{}) { - l.t.Log(v...) -} - -func (l testLogger) Printf(format string, v ...interface{}) { - l.t.Logf(format, v...) -} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go index 876740de5d7..7d060279c95 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/web_service_test.go @@ -108,20 +108,6 @@ func TestContentType415_POST_Issue170(t *testing.T) { } } -// go test -v -test.run TestContentType406PlainJson ...restful -func TestContentType406PlainJson(t *testing.T) { - tearDown() - TraceLogger(testLogger{t}) - Add(newGetPlainTextOrJsonService()) - httpRequest, _ := http.NewRequest("GET", "http://here.com/get", nil) - httpRequest.Header.Set("Accept", "text/plain") - httpWriter := httptest.NewRecorder() - DefaultContainer.dispatch(httpWriter, httpRequest) - if got, want := httpWriter.Code, 200; got != want { - t.Errorf("got %v, want %v", got, want) - } -} - // go test -v -test.run TestContentTypeOctet_Issue170 ...restful func TestContentTypeOctet_Issue170(t *testing.T) { tearDown() @@ -169,13 +155,6 @@ func newGetOnlyJsonOnlyService() *WebService { return ws } -func newGetPlainTextOrJsonService() *WebService { - ws := new(WebService).Path("") - ws.Produces("text/plain", "application/json") - ws.Route(ws.GET("/get").To(doNothing)) - return ws -} - func newGetConsumingOctetStreamService() *WebService { ws := new(WebService).Path("") ws.Consumes("application/octet-stream") diff --git a/pkg/api/validation/schema.go b/pkg/api/validation/schema.go index 005991d1896..0bfaa1c9c2a 100644 --- a/pkg/api/validation/schema.go +++ b/pkg/api/validation/schema.go @@ -85,12 +85,12 @@ func (s *SwaggerSchema) ValidateBytes(data []byte) error { func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, typeName string) error { models := s.api.Models // TODO: handle required fields here too. - model, ok := models.At(typeName) + model, ok := models[typeName] if !ok { return fmt.Errorf("couldn't find type: %s", typeName) } properties := model.Properties - if len(properties.List) == 0 { + if len(properties) == 0 { // The object does not have any sub-fields. return nil } @@ -102,7 +102,7 @@ func (s *SwaggerSchema) ValidateObject(obj interface{}, apiVersion, fieldName, t fieldName = fieldName + "." } for key, value := range fields { - details, ok := properties.At(key) + details, ok := properties[key] if !ok { glog.Infof("unknown field: %s", key) // Some properties can be missing because of