From 2be1cbe0af93e59fa9b217021409eac18e7ae207 Mon Sep 17 00:00:00 2001 From: Anastasis Andronidis Date: Fri, 17 Jul 2015 00:21:58 +0200 Subject: [PATCH] updated go-restful --- Godeps/Godeps.json | 4 +- .../emicklei/go-restful/container.go | 15 +- .../github.com/emicklei/go-restful/install.sh | 2 +- .../emicklei/go-restful/response.go | 5 +- .../emicklei/go-restful/response_test.go | 19 +++ .../emicklei/go-restful/swagger/README.md | 50 ++++++- .../go-restful/swagger/model_builder.go | 82 ++++++++--- .../go-restful/swagger/model_builder_test.go | 131 ++++++++++++++++++ .../go-restful/swagger/model_property_ext.go | 59 ++++++++ .../swagger/model_property_ext_test.go | 46 ++++++ .../go-restful/swagger/swagger_test.go | 18 +++ .../go-restful/swagger/swagger_webservice.go | 3 +- .../emicklei/go-restful/web_service_test.go | 14 ++ 13 files changed, 419 insertions(+), 29 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext.go create mode 100644 Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index ce35855f176..7bf3789be04 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -199,8 +199,8 @@ }, { "ImportPath": "github.com/emicklei/go-restful", - "Comment": "v1.1.3-54-gbdfb7d4", - "Rev": "bdfb7d41639a84ea7c36df648e5865cd9fbf21e2" + "Comment": "v1.1.3-76-gbfd6ff2", + "Rev": "bfd6ff29d2961031cec64346a92bae4cde96c868" }, { "ImportPath": "github.com/evanphx/json-patch", diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/container.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/container.go index 876ade8cce1..fd2e12ca44c 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/container.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/container.go @@ -150,11 +150,20 @@ func writeServiceError(err ServiceError, req *Request, resp *Response) { // Dispatch the incoming Http Request to a matching WebService. func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { + writer := httpWriter + + // CompressingResponseWriter should be closed after all operations are done + defer func() { + if compressWriter, ok := writer.(*CompressingResponseWriter); ok { + compressWriter.Close() + } + }() + // Instal panic recovery unless told otherwise if !c.doNotRecover { // catch all for 500 response defer func() { if r := recover(); r != nil { - c.recoverHandleFunc(r, httpWriter) + c.recoverHandleFunc(r, writer) return } }() @@ -168,7 +177,6 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R // Detect if compression is needed // assume without compression, test for override - writer := httpWriter if c.contentEncodingEnabled { doCompress, encoding := wantsCompressedResponse(httpRequest) if doCompress { @@ -179,9 +187,6 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R httpWriter.WriteHeader(http.StatusInternalServerError) return } - defer func() { - writer.(*CompressingResponseWriter).Close() - }() } } // Find best match Route ; err is non nil if no match was found diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/install.sh b/Godeps/_workspace/src/github.com/emicklei/go-restful/install.sh index b5de8a2c123..5fe03b56978 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/install.sh +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/install.sh @@ -1,5 +1,5 @@ cd examples - ls *.go | xargs -I {} go build {} + ls *.go | xargs -I {} go build -o /tmp/ignore {} cd .. go fmt ...swagger && \ go test -test.v ...swagger && \ diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/response.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/response.go index 04e21bbe496..a33f14248c5 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/response.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/response.go @@ -209,9 +209,10 @@ func (r *Response) WriteErrorString(status int, errorReason string) error { // 204 (http.StatusNoContent) or 304 (http.StatusNotModified)) func (r *Response) WriteHeader(httpStatus int) { r.statusCode = httpStatus - // if 204 then WriteEntity will not be called so we need to pass this code + // if 201,204,304 then WriteEntity will not be called so we need to pass this code if http.StatusNoContent == httpStatus || - http.StatusNotModified == httpStatus { + http.StatusNotModified == httpStatus || + http.StatusPartialContent == httpStatus { r.ResponseWriter.WriteHeader(httpStatus) } } diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go index ee9a74f7aa9..6caa4d5274f 100644 --- a/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go @@ -68,6 +68,25 @@ func TestMeasureContentLengthWriteErrorString(t *testing.T) { } } +// go test -v -test.run TestStatusIsPassedToResponse ...restful +func TestStatusIsPassedToResponse(t *testing.T) { + for _, each := range []struct { + write, read int + }{ + {write: 204, read: 204}, + {write: 304, read: 304}, + {write: 200, read: 200}, + {write: 400, read: 200}, + } { + httpWriter := httptest.NewRecorder() + resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true} + resp.WriteHeader(each.write) + if got, want := httpWriter.Code, each.read; got != want { + t.Error("got %v want %v", got, want) + } + } +} + // go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) { httpWriter := httptest.NewRecorder() 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..6c27c307061 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 @@ -21,8 +21,56 @@ Now, you can install the Swagger WebService for serving the Swagger specificatio swagger.InstallSwaggerService(config) +Documenting Structs +-- + +Currently there are 2 ways to document your structs in the go-restful Swagger. + +###### By using struct tags +- Use tag "description" to annotate a struct field with a description to show in the UI +- Use tag "modelDescription" to annotate the struct itself with a description to show in the UI. The tag can be added in an field of the struct and in case that there are multiple definition, they will be appended with an empty line. + +###### By using the SwaggerDoc method +Here is an example with an `Address` struct and the documentation for each of the fields. The `""` is a special entry for **documenting the struct itself**. + + type Address struct { + Country string `json:"country,omitempty"` + PostCode int `json:"postcode,omitempty"` + } + + func (Address) SwaggerDoc() map[string]string { + return map[string]string{ + "": "Address doc", + "country": "Country doc", + "postcode": "PostCode doc", + } + } + +This example will generate a JSON like this + + { + "Address": { + "id": "Address", + "description": "Address doc", + "properties": { + "country": { + "type": "string", + "description": "Country doc" + }, + "postcode": { + "type": "integer", + "format": "int32", + "description": "PostCode doc" + } + } + } + } + +**Very Important Notes:** +- `SwaggerDoc()` is using a **NON-Pointer** receiver (e.g. func (Address) and not func (*Address)) +- The returned map should use as key the name of the field as defined in the JSON parameter (e.g. `"postcode"` and not `"PostCode"`) + Notes -- - The Nickname of an Operation is automatically set by finding the name of the function. You can override it using RouteBuilder.Operation(..) - 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 2ecc49a6c16..a4135895a11 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 @@ -16,6 +16,19 @@ type modelBuilder struct { Models *ModelList } +type documentable interface { + SwaggerDoc() map[string]string +} + +// Check if this structure has a method with signature func () SwaggerDoc() map[string]string +// If it exists, retrive the documentation and overwrite all struct tag descriptions +func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string { + if docable, ok := reflect.New(model).Elem().Interface().(documentable); ok { + return docable.SwaggerDoc() + } + return make(map[string]string) +} + // addModelFrom creates and adds a Model to the builder and detects and calls // the post build hook for customizations func (b modelBuilder) addModelFrom(sample interface{}) { @@ -58,14 +71,23 @@ func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model { if st.Kind() != reflect.Struct { return &sm } + + fullDoc := getDocFromMethodSwaggerDoc2(st) + modelDescriptions := []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 + jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName) + if len(modelDescription) > 0 { + modelDescriptions = append(modelDescriptions, modelDescription) } - // add if not ommitted + + // add if not omitted if len(jsonName) != 0 { + // update description + if fieldDoc, ok := fullDoc[jsonName]; ok { + prop.Description = fieldDoc + } // update Required if b.isPropertyRequired(field) { sm.Required = append(sm.Required, jsonName) @@ -73,6 +95,15 @@ func (b modelBuilder) addModel(st reflect.Type, nameOverride string) *Model { sm.Properties.Put(jsonName, prop) } } + + // We always overwrite documentation if SwaggerDoc method exists + // "" is special for documenting the struct itself + if modelDoc, ok := fullDoc[""]; ok { + sm.Description = modelDoc + } else if len(modelDescriptions) != 0 { + sm.Description = strings.Join(modelDescriptions, "\n") + } + // update model builder with completed model b.Models.Put(modelName, sm) @@ -90,21 +121,32 @@ func (b modelBuilder) isPropertyRequired(field reflect.StructField) bool { return required } -func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, modelName string) (jsonName string, prop ModelProperty) { +func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, modelName string) (jsonName, modelDescription string, prop ModelProperty) { jsonName = b.jsonNameOfField(field) if len(jsonName) == 0 { // empty name signals skip property - return "", prop + return "", "", prop } + + if tag := field.Tag.Get("modelDescription"); tag != "" { + modelDescription = tag + } + fieldType := field.Type + prop.setPropertyMetadata(field) + // check if type is doing its own marshalling marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem() if fieldType.Implements(marshalerType) { var pType = "string" - prop.Type = &pType - prop.Format = b.jsonSchemaFormat(fieldType.String()) - return jsonName, prop + if prop.Type == nil { + prop.Type = &pType + } + if prop.Format == "" { + prop.Format = b.jsonSchemaFormat(fieldType.String()) + } + return jsonName, modelDescription, prop } // check if annotation says it is a string @@ -113,34 +155,37 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod if len(s) > 1 && s[1] == "string" { stringt := "string" prop.Type = &stringt - return jsonName, prop + return jsonName, modelDescription, prop } } fieldKind := fieldType.Kind() switch { case fieldKind == reflect.Struct: - return b.buildStructTypeProperty(field, jsonName, model) + jsonName, prop := b.buildStructTypeProperty(field, jsonName, model) + return jsonName, modelDescription, prop case fieldKind == reflect.Slice || fieldKind == reflect.Array: - return b.buildArrayTypeProperty(field, jsonName, modelName) + jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName) + return jsonName, modelDescription, prop case fieldKind == reflect.Ptr: - return b.buildPointerTypeProperty(field, jsonName, modelName) + jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName) + return jsonName, modelDescription, prop case fieldKind == reflect.String: stringt := "string" prop.Type = &stringt - return jsonName, prop + return jsonName, modelDescription, prop case fieldKind == reflect.Map: // if it's a map, it's unstructured, and swagger 1.2 can't handle it anyt := "any" prop.Type = &anyt - return jsonName, prop + return jsonName, modelDescription, prop } if b.isPrimitiveType(fieldType.String()) { mapped := b.jsonSchemaType(fieldType.String()) prop.Type = &mapped prop.Format = b.jsonSchemaFormat(fieldType.String()) - return jsonName, prop + return jsonName, modelDescription, prop } modelType := fieldType.String() prop.Ref = &modelType @@ -150,7 +195,7 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod prop.Ref = &nestedTypeName b.addModel(fieldType, nestedTypeName) } - return jsonName, prop + return jsonName, modelDescription, prop } func hasNamedJSONTag(field reflect.StructField) bool { @@ -168,6 +213,7 @@ func hasNamedJSONTag(field reflect.StructField) bool { func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *Model) (nameJson string, prop ModelProperty) { fieldType := field.Type + prop.setPropertyMetadata(field) // check for anonymous if len(fieldType.Name()) == 0 { // anonymous @@ -218,6 +264,7 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) { fieldType := field.Type + prop.setPropertyMetadata(field) var pType = "array" prop.Type = &pType elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem()) @@ -238,6 +285,7 @@ func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName func (b modelBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop ModelProperty) { fieldType := field.Type + prop.setPropertyMetadata(field) // override type of pointer to list-likes if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array { 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 6068aaf0b59..2f1eeeffff2 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 @@ -978,3 +978,134 @@ func TestEmbeddedStructPull204(t *testing.T) { } `) } + +type AddressWithMethod struct { + Country string `json:"country,omitempty"` + PostCode int `json:"postcode,omitempty"` +} + +func (AddressWithMethod) SwaggerDoc() map[string]string { + return map[string]string{ + "": "Address doc", + "country": "Country doc", + "postcode": "PostCode doc", + } +} + +func TestDocInMethodSwaggerDoc(t *testing.T) { + expected := `{ + "swagger.AddressWithMethod": { + "id": "swagger.AddressWithMethod", + "description": "Address doc", + "properties": { + "country": { + "type": "string", + "description": "Country doc" + }, + "postcode": { + "type": "integer", + "format": "int32", + "description": "PostCode doc" + } + } + } + }` + testJsonFromStruct(t, AddressWithMethod{}, expected) +} + +type RefDesc struct { + f1 *int64 `description:"desc"` +} + +func TestPtrDescription(t *testing.T) { + b := RefDesc{} + expected := `{ + "swagger.RefDesc": { + "id": "swagger.RefDesc", + "required": [ + "f1" + ], + "properties": { + "f1": { + "type": "integer", + "format": "int64", + "description": "desc" + } + } + } + }` + testJsonFromStruct(t, b, expected) +} + +type A struct { + B `json:",inline"` + C1 `json:"metadata,omitempty"` +} + +type B struct { + SB string +} + +type C1 struct { + SC string +} + +func (A) SwaggerDoc() map[string]string { + return map[string]string{ + "": "A struct", + "B": "B field", // We should not get anything from this + "metadata": "C1 field", + } +} + +func (B) SwaggerDoc() map[string]string { + return map[string]string{ + "": "B struct", + "SB": "SB field", + } +} + +func (C1) SwaggerDoc() map[string]string { + return map[string]string{ + "": "C1 struct", + "SC": "SC field", + } +} + +func TestNestedStructDescription(t *testing.T) { + expected := ` +{ + "swagger.A": { + "id": "swagger.A", + "description": "A struct", + "required": [ + "SB" + ], + "properties": { + "SB": { + "type": "string", + "description": "SB field" + }, + "metadata": { + "$ref": "swagger.C1", + "description": "C1 field" + } + } + }, + "swagger.C1": { + "id": "swagger.C1", + "description": "C1 struct", + "required": [ + "SC" + ], + "properties": { + "SC": { + "type": "string", + "description": "SC field" + } + } + } + } +` + testJsonFromStruct(t, A{}, expected) +} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext.go new file mode 100644 index 00000000000..e44809e24c0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext.go @@ -0,0 +1,59 @@ +package swagger + +import ( + "reflect" + "strings" +) + +func (prop *ModelProperty) setDescription(field reflect.StructField) { + if tag := field.Tag.Get("description"); tag != "" { + prop.Description = tag + } +} + +func (prop *ModelProperty) setDefaultValue(field reflect.StructField) { + if tag := field.Tag.Get("default"); tag != "" { + prop.DefaultValue = Special(tag) + } +} + +func (prop *ModelProperty) setEnumValues(field reflect.StructField) { + // We use | to separate the enum values. This value is chosen + // since its unlikely to be useful in actual enumeration values. + if tag := field.Tag.Get("enum"); tag != "" { + prop.Enum = strings.Split(tag, "|") + } +} + +func (prop *ModelProperty) setMaximum(field reflect.StructField) { + if tag := field.Tag.Get("maximum"); tag != "" { + prop.Maximum = tag + } +} + +func (prop *ModelProperty) setMinimum(field reflect.StructField) { + if tag := field.Tag.Get("minimum"); tag != "" { + prop.Minimum = tag + } +} + +func (prop *ModelProperty) setUniqueItems(field reflect.StructField) { + tag := field.Tag.Get("unique") + switch tag { + case "true": + v := true + prop.UniqueItems = &v + case "false": + v := false + prop.UniqueItems = &v + } +} + +func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) { + prop.setDescription(field) + prop.setEnumValues(field) + prop.setMinimum(field) + prop.setMaximum(field) + prop.setUniqueItems(field) + prop.setDefaultValue(field) +} diff --git a/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go new file mode 100644 index 00000000000..3fb0748e374 --- /dev/null +++ b/Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext_test.go @@ -0,0 +1,46 @@ +package swagger + +import "testing" + +// clear && go test -v -test.run TestThatExtraTagsAreReadIntoModel ...swagger +func TestThatExtraTagsAreReadIntoModel(t *testing.T) { + type Anything struct { + Name string `description:"name" modelDescription:"a test"` + Size int `minimum:"0" maximum:"10"` + Stati string `enum:"off|on" default:"on" modelDescription:"more description"` + ID string `unique:"true"` + Password string + } + m := modelsFromStruct(Anything{}) + props, _ := m.At("swagger.Anything") + p1, _ := props.Properties.At("Name") + if got, want := p1.Description, "name"; got != want { + t.Errorf("got %v want %v", got, want) + } + p2, _ := props.Properties.At("Size") + if got, want := p2.Minimum, "0"; got != want { + t.Errorf("got %v want %v", got, want) + } + if got, want := p2.Maximum, "10"; got != want { + t.Errorf("got %v want %v", got, want) + } + p3, _ := props.Properties.At("Stati") + if got, want := p3.Enum[0], "off"; got != want { + t.Errorf("got %v want %v", got, want) + } + if got, want := p3.Enum[1], "on"; got != want { + t.Errorf("got %v want %v", got, want) + } + p4, _ := props.Properties.At("ID") + if got, want := *p4.UniqueItems, true; got != want { + t.Errorf("got %v want %v", got, want) + } + p5, _ := props.Properties.At("Password") + if got, want := *p5.Type, "string"; got != want { + t.Errorf("got %v want %v", got, want) + } + + if got, want := props.Description, "a test\nmore description"; got != want { + t.Errorf("got %v want %v", got, want) + } +} 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..8b8632ae379 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 @@ -6,6 +6,24 @@ import ( "github.com/emicklei/go-restful" ) +// go test -v -test.run TestThatMultiplePathsOnRootAreHandled ...swagger +func TestThatMultiplePathsOnRootAreHandled(t *testing.T) { + ws1 := new(restful.WebService) + ws1.Route(ws1.GET("/_ping").To(dummy)) + ws1.Route(ws1.GET("/version").To(dummy)) + + cfg := Config{ + WebServicesUrl: "http://here.com", + ApiPath: "/apipath", + WebServices: []*restful.WebService{ws1}, + } + sws := newSwaggerService(cfg) + decl := sws.composeDeclaration(ws1, "/") + if got, want := len(decl.Apis), 2; got != want { + t.Errorf("got %v want %v", got, want) + } +} + // go test -v -test.run TestServiceToApi ...swagger func TestServiceToApi(t *testing.T) { ws := new(restful.WebService) 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..d8585b4f4ee 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 @@ -178,11 +178,12 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re resp.WriteAsJson(decl) } +// composeDeclaration uses all routes and parameters to create a ApiDeclaration func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix string) ApiDeclaration { decl := ApiDeclaration{ SwaggerVersion: swaggerVersion, BasePath: sws.config.WebServicesUrl, - ResourcePath: ws.RootPath(), + ResourcePath: pathPrefix, Models: ModelList{}, ApiVersion: ws.Version()} 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..d2e504c0ca2 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 @@ -50,6 +50,20 @@ func TestCapturePanic(t *testing.T) { } } +func TestCapturePanicWithEncoded(t *testing.T) { + tearDown() + Add(newPanicingService()) + DefaultContainer.EnableContentEncoding(true) + httpRequest, _ := http.NewRequest("GET", "http://here.com/fire", nil) + httpRequest.Header.Set("Accept", "*/*") + httpRequest.Header.Set("Accept-Encoding", "gzip") + httpWriter := httptest.NewRecorder() + DefaultContainer.dispatch(httpWriter, httpRequest) + if 500 != httpWriter.Code { + t.Error("500 expected on fire, got", httpWriter.Code) + } +} + func TestNotFound(t *testing.T) { tearDown() httpRequest, _ := http.NewRequest("GET", "http://here.com/missing", nil)