diff --git a/extensions.go b/extensions.go new file mode 100644 index 00000000..5023fa04 --- /dev/null +++ b/extensions.go @@ -0,0 +1,21 @@ +package restful + +// Copyright 2021 Ernest Micklei. All rights reserved. +// Use of this source code is governed by a license +// that can be found in the LICENSE file. + +// ExtensionProperties provides storage of vendor extensions for entities +type ExtensionProperties struct { + // Extensions vendor extensions used to describe extra functionality + // (https://swagger.io/docs/specification/2-0/swagger-extensions/) + Extensions map[string]interface{} +} + +// AddExtension adds or updates a key=value pair to the extension map. +func (ep *ExtensionProperties) AddExtension(key string, value interface{}) { + if ep.Extensions == nil { + ep.Extensions = map[string]interface{}{key: value} + } else { + ep.Extensions[key] = value + } +} diff --git a/parameter.go b/parameter.go index e8793304..c5555a86 100644 --- a/parameter.go +++ b/parameter.go @@ -52,6 +52,7 @@ type Parameter struct { // ParameterData represents the state of a Parameter. // It is made public to make it accessible to e.g. the Swagger package. type ParameterData struct { + ExtensionProperties Name, Description, DataType, DataFormat string Kind int Required bool diff --git a/route.go b/route.go index 6ac26129..f5661c90 100644 --- a/route.go +++ b/route.go @@ -19,6 +19,7 @@ type RouteSelectionConditionFunction func(httpRequest *http.Request) bool // Route binds a HTTP Method,Path,Consumes combination to a RouteFunction. type Route struct { + ExtensionProperties Method string Produces []string Consumes []string diff --git a/route_builder.go b/route_builder.go index d22fe1fb..23641b6d 100644 --- a/route_builder.go +++ b/route_builder.go @@ -38,6 +38,7 @@ type RouteBuilder struct { errorMap map[int]ResponseError defaultResponse *ResponseError metadata map[string]interface{} + extensions map[string]interface{} deprecated bool contentEncodingEnabled *bool } @@ -204,13 +205,22 @@ func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder { return b } +// AddExtension adds or updates a key=value pair to the extensions map. +func (b *RouteBuilder) AddExtension(key string, value interface{}) *RouteBuilder { + if b.extensions == nil { + b.extensions = map[string]interface{}{} + } + b.extensions[key] = value + return b +} + // Deprecate sets the value of deprecated to true. Deprecated routes have a special UI treatment to warn against use func (b *RouteBuilder) Deprecate() *RouteBuilder { b.deprecated = true return b } -// AllowedMethodsWithoutContentType overides the default list GET,HEAD,OPTIONS,DELETE,TRACE +// AllowedMethodsWithoutContentType overrides the default list GET,HEAD,OPTIONS,DELETE,TRACE // If a request does not include a content-type header then // depending on the method, it may return a 415 Unsupported Media. // Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,... @@ -221,6 +231,7 @@ func (b *RouteBuilder) AllowedMethodsWithoutContentType(methods []string) *Route // ResponseError represents a response; not necessarily an error. type ResponseError struct { + ExtensionProperties Code int Message string Model interface{} @@ -335,6 +346,7 @@ func (b *RouteBuilder) Build() Route { contentEncodingEnabled: b.contentEncodingEnabled, allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType, } + route.Extensions = b.extensions route.postBuild() return route } diff --git a/route_builder_test.go b/route_builder_test.go index 17258d6e..933a423a 100644 --- a/route_builder_test.go +++ b/route_builder_test.go @@ -42,7 +42,13 @@ func TestRouteBuilder(t *testing.T) { json := "application/json" b := new(RouteBuilder) b.To(dummy) - b.Path("/routes").Method("HEAD").Consumes(json).Produces(json).Metadata("test", "test-value").DefaultReturns("default", time.Now()) + b.Path("/routes"). + Method("HEAD"). + Consumes(json). + Produces(json). + Metadata("test", "test-value"). + AddExtension("x-restful-test", "test-value"). + DefaultReturns("default", time.Now()) r := b.Build() if r.Path != "/routes" { t.Error("path invalid") @@ -59,6 +65,9 @@ func TestRouteBuilder(t *testing.T) { if r.Metadata["test"] != "test-value" { t.Errorf("Metadata not set") } + if r.Extensions["x-restful-test"] != "test-value" { + t.Errorf("Extensions not set") + } if r.DefaultResponse == nil { t.Fatal("expected default response") } @@ -91,13 +100,13 @@ func TestContentEncodingEnabled(t *testing.T) { r := b.Build() got := r.contentEncodingEnabled - var want *bool //nil + var want *bool // nil if got != want { t.Errorf("got %v want %v (default nil)", got, want) } - //true + // true b = new(RouteBuilder) b.function = dummy b.ContentEncodingEnabled(true) @@ -108,7 +117,7 @@ func TestContentEncodingEnabled(t *testing.T) { t.Errorf("got %v want %v (explicit true)", *got, true) } - //true + // true b = new(RouteBuilder) b.function = dummy b.ContentEncodingEnabled(false)