diff --git a/vendor/github.com/emicklei/go-restful-openapi/build_path.go b/vendor/github.com/emicklei/go-restful-openapi/build_path.go
index 741b49aa0..6d0fa8ccc 100644
--- a/vendor/github.com/emicklei/go-restful-openapi/build_path.go
+++ b/vendor/github.com/emicklei/go-restful-openapi/build_path.go
@@ -199,7 +199,14 @@ func buildResponse(e restful.ResponseError, cfg Config) (r spec.Response) {
}
} else {
modelName := definitionBuilder{}.keyFrom(st)
- r.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
+ if isPrimitiveType(modelName) {
+ // If the response is a primitive type, then don't reference any definitions.
+ // Instead, set the schema's "type" to the model name.
+ r.Schema.AddType(modelName, "")
+ } else {
+ modelName := definitionBuilder{}.keyFrom(st)
+ r.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
+ }
}
}
return r
diff --git a/vendor/github.com/emicklei/go-restful-openapi/definition_builder.go b/vendor/github.com/emicklei/go-restful-openapi/definition_builder.go
index 54e761085..e4a4d26d2 100644
--- a/vendor/github.com/emicklei/go-restful-openapi/definition_builder.go
+++ b/vendor/github.com/emicklei/go-restful-openapi/definition_builder.go
@@ -196,9 +196,7 @@ func (b definitionBuilder) buildProperty(field reflect.StructField, model *spec.
prop.Type = []string{stringt}
return jsonName, modelDescription, prop
case fieldKind == reflect.Map:
- // if it's a map, it's unstructured, and swagger can't handle it
- objectType := "object"
- prop.Type = []string{objectType}
+ jsonName, prop := b.buildMapTypeProperty(field, jsonName, modelName)
return jsonName, modelDescription, prop
}
@@ -315,6 +313,38 @@ func (b definitionBuilder) buildArrayTypeProperty(field reflect.StructField, jso
return jsonName, prop
}
+func (b definitionBuilder) buildMapTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
+ setPropertyMetadata(&prop, field)
+ fieldType := field.Type
+ var pType = "object"
+ prop.Type = []string{pType}
+
+ // As long as the element isn't an interface, we should be able to figure out what the
+ // intended type is and represent it in `AdditionalProperties`.
+ // See: https://swagger.io/docs/specification/data-models/dictionaries/
+ if fieldType.Elem().Kind().String() != "interface" {
+ isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
+ elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
+ prop.AdditionalProperties = &spec.SchemaOrBool{
+ Schema: &spec.Schema{},
+ }
+ if isPrimitive {
+ mapped := b.jsonSchemaType(elemTypeName)
+ prop.AdditionalProperties.Schema.Type = []string{mapped}
+ } else {
+ prop.AdditionalProperties.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
+ }
+ // add|overwrite model for element type
+ if fieldType.Elem().Kind() == reflect.Ptr {
+ fieldType = fieldType.Elem()
+ }
+ if !isPrimitive {
+ b.addModel(fieldType.Elem(), elemTypeName)
+ }
+ }
+ return jsonName, prop
+}
+
func (b definitionBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
setPropertyMetadata(&prop, field)
fieldType := field.Type
@@ -330,7 +360,7 @@ func (b definitionBuilder) buildPointerTypeProperty(field reflect.StructField, j
}
if isPrimitive {
primName := b.jsonSchemaType(elemName)
- prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + primName)
+ prop.Items.Schema.Type = []string{primName}
} else {
prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemName)
}
diff --git a/vendor/github.com/emicklei/go-restful-openapi/examples/user-resource.go b/vendor/github.com/emicklei/go-restful-openapi/examples/user-resource.go
index 73901be08..b5203acf4 100644
--- a/vendor/github.com/emicklei/go-restful-openapi/examples/user-resource.go
+++ b/vendor/github.com/emicklei/go-restful-openapi/examples/user-resource.go
@@ -123,9 +123,8 @@ func main() {
restful.DefaultContainer.Add(u.WebService())
config := restfulspec.Config{
- WebServices: restful.RegisteredWebServices(), // you control what services are visible
- WebServicesURL: "http://localhost:8080",
- APIPath: "/apidocs.json",
+ WebServices: restful.RegisteredWebServices(), // you control what services are visible
+ APIPath: "/apidocs.json",
PostBuildSwaggerObjectHandler: enrichSwaggerObject}
restful.DefaultContainer.Add(restfulspec.NewOpenAPIService(config))
diff --git a/vendor/github.com/emicklei/go-restful-openapi/property_ext.go b/vendor/github.com/emicklei/go-restful-openapi/property_ext.go
index 5aca3b65f..eb91208bc 100644
--- a/vendor/github.com/emicklei/go-restful-openapi/property_ext.go
+++ b/vendor/github.com/emicklei/go-restful-openapi/property_ext.go
@@ -82,6 +82,16 @@ func setUniqueItems(prop *spec.Schema, field reflect.StructField) {
}
}
+func setReadOnly(prop *spec.Schema, field reflect.StructField) {
+ tag := field.Tag.Get("readOnly")
+ switch tag {
+ case "true":
+ prop.ReadOnly = true
+ case "false":
+ prop.ReadOnly = false
+ }
+}
+
func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) {
setDescription(prop, field)
setDefaultValue(prop, field)
@@ -90,4 +100,5 @@ func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) {
setMaximum(prop, field)
setUniqueItems(prop, field)
setType(prop, field)
+ setReadOnly(prop, field)
}
diff --git a/vendor/github.com/emicklei/go-restful-openapi/spec_resource.go b/vendor/github.com/emicklei/go-restful-openapi/spec_resource.go
index 1e62d64f5..b36274061 100644
--- a/vendor/github.com/emicklei/go-restful-openapi/spec_resource.go
+++ b/vendor/github.com/emicklei/go-restful-openapi/spec_resource.go
@@ -30,6 +30,13 @@ func BuildSwagger(config Config) *spec.Swagger {
for _, each := range config.WebServices {
for path, item := range buildPaths(each, config).Paths {
+ existingPathItem, ok := paths.Paths[path]
+ if ok {
+ for _, r := range each.Routes() {
+ _, patterns := sanitizePath(r.Path)
+ item = buildPathItem(each, r, existingPathItem, patterns, config)
+ }
+ }
paths.Paths[path] = item
}
for name, def := range buildDefinitions(each, config) {
diff --git a/vendor/github.com/marccarre/go-restful-openapi/LICENSE b/vendor/github.com/marccarre/go-restful-openapi/LICENSE
new file mode 100644
index 000000000..aeab5b440
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2017 Ernest Micklei
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/vendor/github.com/marccarre/go-restful-openapi/build_definitions.go b/vendor/github.com/marccarre/go-restful-openapi/build_definitions.go
new file mode 100644
index 000000000..95452554e
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/build_definitions.go
@@ -0,0 +1,32 @@
+package restfulspec
+
+import (
+ "reflect"
+
+ restful "github.com/emicklei/go-restful"
+ "github.com/go-openapi/spec"
+)
+
+func buildDefinitions(ws *restful.WebService, cfg Config) (definitions spec.Definitions) {
+ definitions = spec.Definitions{}
+ for _, each := range ws.Routes() {
+ addDefinitionsFromRouteTo(each, cfg, definitions)
+ }
+ return
+}
+
+func addDefinitionsFromRouteTo(r restful.Route, cfg Config, d spec.Definitions) {
+ builder := definitionBuilder{Definitions: d, Config: cfg}
+ if r.ReadSample != nil {
+ builder.addModel(reflect.TypeOf(r.ReadSample), "")
+ }
+ if r.WriteSample != nil {
+ builder.addModel(reflect.TypeOf(r.WriteSample), "")
+ }
+ for _, v := range r.ResponseErrors {
+ if v.Model == nil {
+ continue
+ }
+ builder.addModel(reflect.TypeOf(v.Model), "")
+ }
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/build_path.go b/vendor/github.com/marccarre/go-restful-openapi/build_path.go
new file mode 100644
index 000000000..6d0fa8ccc
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/build_path.go
@@ -0,0 +1,254 @@
+package restfulspec
+
+import (
+ "net/http"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+
+ restful "github.com/emicklei/go-restful"
+ "github.com/go-openapi/spec"
+)
+
+// KeyOpenAPITags is a Metadata key for a restful Route
+const KeyOpenAPITags = "openapi.tags"
+
+func buildPaths(ws *restful.WebService, cfg Config) spec.Paths {
+ p := spec.Paths{Paths: map[string]spec.PathItem{}}
+ for _, each := range ws.Routes() {
+ path, patterns := sanitizePath(each.Path)
+ existingPathItem, ok := p.Paths[path]
+ if !ok {
+ existingPathItem = spec.PathItem{}
+ }
+ p.Paths[path] = buildPathItem(ws, each, existingPathItem, patterns, cfg)
+ }
+ return p
+}
+
+// sanitizePath removes regex expressions from named path params,
+// since openapi only supports setting the pattern as a a property named "pattern".
+// Expressions like "/api/v1/{name:[a-z]/" are converted to "/api/v1/{name}/".
+// The second return value is a map which contains the mapping from the path parameter
+// name to the extracted pattern
+func sanitizePath(restfulPath string) (string, map[string]string) {
+ openapiPath := ""
+ patterns := map[string]string{}
+ for _, fragment := range strings.Split(restfulPath, "/") {
+ if fragment == "" {
+ continue
+ }
+ if strings.HasPrefix(fragment, "{") && strings.Contains(fragment, ":") {
+ split := strings.Split(fragment, ":")
+ fragment = split[0][1:]
+ pattern := split[1][:len(split[1])-1]
+ patterns[fragment] = pattern
+ fragment = "{" + fragment + "}"
+ }
+ openapiPath += "/" + fragment
+ }
+ return openapiPath, patterns
+}
+
+func buildPathItem(ws *restful.WebService, r restful.Route, existingPathItem spec.PathItem, patterns map[string]string, cfg Config) spec.PathItem {
+ op := buildOperation(ws, r, patterns, cfg)
+ switch r.Method {
+ case "GET":
+ existingPathItem.Get = op
+ case "POST":
+ existingPathItem.Post = op
+ case "PUT":
+ existingPathItem.Put = op
+ case "DELETE":
+ existingPathItem.Delete = op
+ case "PATCH":
+ existingPathItem.Patch = op
+ case "OPTIONS":
+ existingPathItem.Options = op
+ case "HEAD":
+ existingPathItem.Head = op
+ }
+ return existingPathItem
+}
+
+func buildOperation(ws *restful.WebService, r restful.Route, patterns map[string]string, cfg Config) *spec.Operation {
+ o := spec.NewOperation(r.Operation)
+ o.Description = r.Notes
+ o.Summary = stripTags(r.Doc)
+ o.Consumes = r.Consumes
+ o.Produces = r.Produces
+ o.Deprecated = r.Deprecated
+ if r.Metadata != nil {
+ if tags, ok := r.Metadata[KeyOpenAPITags]; ok {
+ if tagList, ok := tags.([]string); ok {
+ o.Tags = tagList
+ }
+ }
+ }
+ // collect any path parameters
+ for _, param := range ws.PathParameters() {
+ o.Parameters = append(o.Parameters, buildParameter(r, param, patterns[param.Data().Name], cfg))
+ }
+ // route specific params
+ for _, each := range r.ParameterDocs {
+ o.Parameters = append(o.Parameters, buildParameter(r, each, patterns[each.Data().Name], cfg))
+ }
+ o.Responses = new(spec.Responses)
+ props := &o.Responses.ResponsesProps
+ props.StatusCodeResponses = map[int]spec.Response{}
+ for k, v := range r.ResponseErrors {
+ r := buildResponse(v, cfg)
+ props.StatusCodeResponses[k] = r
+ if 200 == k { // any 2xx code?
+ o.Responses.Default = &r
+ }
+ }
+ if len(o.Responses.StatusCodeResponses) == 0 {
+ o.Responses.StatusCodeResponses[200] = spec.Response{ResponseProps: spec.ResponseProps{Description: http.StatusText(http.StatusOK)}}
+ }
+ return o
+}
+
+// stringAutoType automatically picks the correct type from an ambiguously typed
+// string. Ex. numbers become int, true/false become bool, etc.
+func stringAutoType(ambiguous string) interface{} {
+ if ambiguous == "" {
+ return nil
+ }
+ if parsedInt, err := strconv.ParseInt(ambiguous, 10, 64); err == nil {
+ return parsedInt
+ }
+ if parsedBool, err := strconv.ParseBool(ambiguous); err == nil {
+ return parsedBool
+ }
+ return ambiguous
+}
+
+func buildParameter(r restful.Route, restfulParam *restful.Parameter, pattern string, cfg Config) spec.Parameter {
+ p := spec.Parameter{}
+ param := restfulParam.Data()
+ p.In = asParamType(param.Kind)
+ if param.AllowMultiple {
+ p.Type = "array"
+ p.Items = spec.NewItems()
+ p.Items.Type = param.DataType
+ p.CollectionFormat = param.CollectionFormat
+ } else {
+ p.Type = param.DataType
+ }
+ p.Description = param.Description
+ p.Name = param.Name
+ p.Required = param.Required
+
+ if param.Kind == restful.PathParameterKind {
+ p.Pattern = pattern
+ }
+ st := reflect.TypeOf(r.ReadSample)
+ if param.Kind == restful.BodyParameterKind && r.ReadSample != nil && param.DataType == st.String() {
+ p.Schema = new(spec.Schema)
+ p.SimpleSchema = spec.SimpleSchema{}
+ if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
+ dataTypeName := definitionBuilder{}.keyFrom(st.Elem())
+ p.Schema.Type = []string{"array"}
+ p.Schema.Items = &spec.SchemaOrArray{
+ Schema: &spec.Schema{},
+ }
+ isPrimitive := isPrimitiveType(dataTypeName)
+ if isPrimitive {
+ mapped := jsonSchemaType(dataTypeName)
+ p.Schema.Items.Schema.Type = []string{mapped}
+ } else {
+ p.Schema.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + dataTypeName)
+ }
+ } else {
+ p.Schema.Ref = spec.MustCreateRef("#/definitions/" + param.DataType)
+ }
+
+ } else {
+ p.Type = param.DataType
+ p.Default = stringAutoType(param.DefaultValue)
+ p.Format = param.DataFormat
+ }
+
+ return p
+}
+
+func buildResponse(e restful.ResponseError, cfg Config) (r spec.Response) {
+ r.Description = e.Message
+ if e.Model != nil {
+ st := reflect.TypeOf(e.Model)
+ if st.Kind() == reflect.Ptr {
+ // For pointer type, use element type as the key; otherwise we'll
+ // endup with '#/definitions/*Type' which violates openapi spec.
+ st = st.Elem()
+ }
+ r.Schema = new(spec.Schema)
+ if st.Kind() == reflect.Array || st.Kind() == reflect.Slice {
+ modelName := definitionBuilder{}.keyFrom(st.Elem())
+ r.Schema.Type = []string{"array"}
+ r.Schema.Items = &spec.SchemaOrArray{
+ Schema: &spec.Schema{},
+ }
+ isPrimitive := isPrimitiveType(modelName)
+ if isPrimitive {
+ mapped := jsonSchemaType(modelName)
+ r.Schema.Items.Schema.Type = []string{mapped}
+ } else {
+ r.Schema.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
+ }
+ } else {
+ modelName := definitionBuilder{}.keyFrom(st)
+ if isPrimitiveType(modelName) {
+ // If the response is a primitive type, then don't reference any definitions.
+ // Instead, set the schema's "type" to the model name.
+ r.Schema.AddType(modelName, "")
+ } else {
+ modelName := definitionBuilder{}.keyFrom(st)
+ r.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
+ }
+ }
+ }
+ return r
+}
+
+// stripTags takes a snippet of HTML and returns only the text content.
+// For example, `<Hi!>
` -> `<Hi!> `.
+func stripTags(html string) string {
+ re := regexp.MustCompile("<[^>]*>")
+ return re.ReplaceAllString(html, "")
+}
+
+func isPrimitiveType(modelName string) bool {
+ if len(modelName) == 0 {
+ return false
+ }
+ return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
+}
+
+func jsonSchemaType(modelName string) string {
+ schemaMap := map[string]string{
+ "uint": "integer",
+ "uint8": "integer",
+ "uint16": "integer",
+ "uint32": "integer",
+ "uint64": "integer",
+
+ "int": "integer",
+ "int8": "integer",
+ "int16": "integer",
+ "int32": "integer",
+ "int64": "integer",
+
+ "byte": "integer",
+ "float64": "number",
+ "float32": "number",
+ "bool": "boolean",
+ "time.Time": "string",
+ }
+ mapped, ok := schemaMap[modelName]
+ if !ok {
+ return modelName // use as is (custom or struct)
+ }
+ return mapped
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/config.go b/vendor/github.com/marccarre/go-restful-openapi/config.go
new file mode 100644
index 000000000..a3fa96f8c
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/config.go
@@ -0,0 +1,41 @@
+package restfulspec
+
+import (
+ "reflect"
+
+ restful "github.com/emicklei/go-restful"
+ "github.com/go-openapi/spec"
+)
+
+// MapSchemaFormatFunc can be used to modify typeName at definition time.
+// To use it set the SchemaFormatHandler in the config.
+type MapSchemaFormatFunc func(typeName string) string
+
+// MapModelTypeNameFunc can be used to return the desired typeName for a given
+// type. It will return false if the default name should be used.
+// To use it set the ModelTypeNameHandler in the config.
+type MapModelTypeNameFunc func(t reflect.Type) (string, bool)
+
+// PostBuildSwaggerObjectFunc can be used to change the creates Swagger Object
+// before serving it. To use it set the PostBuildSwaggerObjectHandler in the config.
+type PostBuildSwaggerObjectFunc func(s *spec.Swagger)
+
+// Config holds service api metadata.
+type Config struct {
+ // WebServicesURL is a DEPRECATED field; it never had any effect in this package.
+ WebServicesURL string
+ // APIPath is the path where the JSON api is avaiable , e.g. /apidocs.json
+ APIPath string
+ // api listing is constructed from this list of restful WebServices.
+ WebServices []*restful.WebService
+ // [optional] on default CORS (Cross-Origin-Resource-Sharing) is enabled.
+ DisableCORS bool
+ // Top-level API version. Is reflected in the resource listing.
+ APIVersion string
+ // [optional] If set, model builder should call this handler to get addition typename-to-swagger-format-field conversion.
+ SchemaFormatHandler MapSchemaFormatFunc
+ // [optional] If set, model builder should call this handler to retrieve the name for a given type.
+ ModelTypeNameHandler MapModelTypeNameFunc
+ // [optional] If set then call this function with the generated Swagger Object
+ PostBuildSwaggerObjectHandler PostBuildSwaggerObjectFunc
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/definition_builder.go b/vendor/github.com/marccarre/go-restful-openapi/definition_builder.go
new file mode 100644
index 000000000..e4a4d26d2
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/definition_builder.go
@@ -0,0 +1,491 @@
+package restfulspec
+
+import (
+ "encoding/json"
+ "reflect"
+ "strings"
+
+ "github.com/go-openapi/spec"
+)
+
+type definitionBuilder struct {
+ Definitions spec.Definitions
+ Config Config
+}
+
+// Documented is
+type Documented interface {
+ SwaggerDoc() map[string]string
+}
+
+// Check if this structure has a method with signature func () SwaggerDoc() map[string]string
+// If it exists, retrieve the documentation and overwrite all struct tag descriptions
+func getDocFromMethodSwaggerDoc2(model reflect.Type) map[string]string {
+ if docable, ok := reflect.New(model).Elem().Interface().(Documented); ok {
+ return docable.SwaggerDoc()
+ }
+ return make(map[string]string)
+}
+
+// addModelFrom creates and adds a Schema to the builder and detects and calls
+// the post build hook for customizations
+func (b definitionBuilder) addModelFrom(sample interface{}) {
+ b.addModel(reflect.TypeOf(sample), "")
+}
+
+func (b definitionBuilder) addModel(st reflect.Type, nameOverride string) *spec.Schema {
+ // Turn pointers into simpler types so further checks are
+ // correct.
+ if st.Kind() == reflect.Ptr {
+ st = st.Elem()
+ }
+
+ modelName := b.keyFrom(st)
+ if nameOverride != "" {
+ modelName = nameOverride
+ }
+ // no models needed for primitive types
+ if b.isPrimitiveType(modelName) {
+ return nil
+ }
+ // golang encoding/json packages says array and slice values encode as
+ // JSON arrays, except that []byte encodes as a base64-encoded string.
+ // If we see a []byte here, treat it at as a primitive type (string)
+ // and deal with it in buildArrayTypeProperty.
+ if (st.Kind() == reflect.Slice || st.Kind() == reflect.Array) &&
+ st.Elem().Kind() == reflect.Uint8 {
+ return nil
+ }
+ // see if we already have visited this model
+ if _, ok := b.Definitions[modelName]; ok {
+ return nil
+ }
+ sm := spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Required: []string{},
+ Properties: map[string]spec.Schema{},
+ },
+ }
+
+ // reference the model before further initializing (enables recursive structs)
+ b.Definitions[modelName] = sm
+
+ // check for slice or array
+ if st.Kind() == reflect.Slice || st.Kind() == reflect.Array {
+ st = st.Elem()
+ }
+ // check for structure or primitive type
+ if st.Kind() != reflect.Struct {
+ return &sm
+ }
+
+ fullDoc := getDocFromMethodSwaggerDoc2(st)
+ modelDescriptions := []string{}
+
+ for i := 0; i < st.NumField(); i++ {
+ field := st.Field(i)
+ jsonName, modelDescription, prop := b.buildProperty(field, &sm, modelName)
+ if len(modelDescription) > 0 {
+ modelDescriptions = append(modelDescriptions, modelDescription)
+ }
+
+ // 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)
+ }
+ sm.Properties[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")
+ }
+ // Needed to pass openapi validation. This field exists for json-schema compatibility,
+ // but it conflicts with the openapi specification.
+ // See https://github.com/go-openapi/spec/issues/23 for more context
+ sm.ID = ""
+
+ // update model builder with completed model
+ b.Definitions[modelName] = sm
+
+ return &sm
+}
+
+func (b definitionBuilder) isPropertyRequired(field reflect.StructField) bool {
+ required := true
+ if optionalTag := field.Tag.Get("optional"); optionalTag == "true" {
+ return false
+ }
+ if jsonTag := field.Tag.Get("json"); jsonTag != "" {
+ s := strings.Split(jsonTag, ",")
+ if len(s) > 1 && s[1] == "omitempty" {
+ return false
+ }
+ }
+ return required
+}
+
+func (b definitionBuilder) buildProperty(field reflect.StructField, model *spec.Schema, modelName string) (jsonName, modelDescription string, prop spec.Schema) {
+ jsonName = b.jsonNameOfField(field)
+ if len(jsonName) == 0 {
+ // empty name signals skip property
+ return "", "", prop
+ }
+
+ if field.Name == "XMLName" && field.Type.String() == "xml.Name" {
+ // property is metadata for the xml.Name attribute, can be skipped
+ return "", "", prop
+ }
+
+ if tag := field.Tag.Get("modelDescription"); tag != "" {
+ modelDescription = tag
+ }
+
+ setPropertyMetadata(&prop, field)
+ if prop.Type != nil {
+ return jsonName, modelDescription, prop
+ }
+ fieldType := field.Type
+
+ // check if type is doing its own marshalling
+ marshalerType := reflect.TypeOf((*json.Marshaler)(nil)).Elem()
+ if fieldType.Implements(marshalerType) {
+ var pType = "string"
+ if prop.Type == nil {
+ prop.Type = []string{pType}
+ }
+ if prop.Format == "" {
+ prop.Format = b.jsonSchemaFormat(b.keyFrom(fieldType))
+ }
+ return jsonName, modelDescription, prop
+ }
+
+ // check if annotation says it is a string
+ if jsonTag := field.Tag.Get("json"); jsonTag != "" {
+ s := strings.Split(jsonTag, ",")
+ if len(s) > 1 && s[1] == "string" {
+ stringt := "string"
+ prop.Type = []string{stringt}
+ return jsonName, modelDescription, prop
+ }
+ }
+
+ fieldKind := fieldType.Kind()
+ switch {
+ case fieldKind == reflect.Struct:
+ jsonName, prop := b.buildStructTypeProperty(field, jsonName, model)
+ return jsonName, modelDescription, prop
+ case fieldKind == reflect.Slice || fieldKind == reflect.Array:
+ jsonName, prop := b.buildArrayTypeProperty(field, jsonName, modelName)
+ return jsonName, modelDescription, prop
+ case fieldKind == reflect.Ptr:
+ jsonName, prop := b.buildPointerTypeProperty(field, jsonName, modelName)
+ return jsonName, modelDescription, prop
+ case fieldKind == reflect.String:
+ stringt := "string"
+ prop.Type = []string{stringt}
+ return jsonName, modelDescription, prop
+ case fieldKind == reflect.Map:
+ jsonName, prop := b.buildMapTypeProperty(field, jsonName, modelName)
+ return jsonName, modelDescription, prop
+ }
+
+ fieldTypeName := b.keyFrom(fieldType)
+ if b.isPrimitiveType(fieldTypeName) {
+ mapped := b.jsonSchemaType(fieldTypeName)
+ prop.Type = []string{mapped}
+ prop.Format = b.jsonSchemaFormat(fieldTypeName)
+ return jsonName, modelDescription, prop
+ }
+ modelType := b.keyFrom(fieldType)
+ prop.Ref = spec.MustCreateRef("#/definitions/" + modelType)
+
+ if fieldType.Name() == "" { // override type of anonymous structs
+ nestedTypeName := modelName + "." + jsonName
+ prop.Ref = spec.MustCreateRef("#/definitions/" + nestedTypeName)
+ b.addModel(fieldType, nestedTypeName)
+ }
+ return jsonName, modelDescription, prop
+}
+
+func hasNamedJSONTag(field reflect.StructField) bool {
+ parts := strings.Split(field.Tag.Get("json"), ",")
+ if len(parts) == 0 {
+ return false
+ }
+ for _, s := range parts[1:] {
+ if s == "inline" {
+ return false
+ }
+ }
+ return len(parts[0]) > 0
+}
+
+func (b definitionBuilder) buildStructTypeProperty(field reflect.StructField, jsonName string, model *spec.Schema) (nameJson string, prop spec.Schema) {
+ setPropertyMetadata(&prop, field)
+ fieldType := field.Type
+ // check for anonymous
+ if len(fieldType.Name()) == 0 {
+ // anonymous
+ anonType := model.ID + "." + jsonName
+ b.addModel(fieldType, anonType)
+ prop.Ref = spec.MustCreateRef("#/definitions/" + anonType)
+ return jsonName, prop
+ }
+
+ if field.Name == fieldType.Name() && field.Anonymous && !hasNamedJSONTag(field) {
+ // embedded struct
+ sub := definitionBuilder{make(spec.Definitions), b.Config}
+ sub.addModel(fieldType, "")
+ subKey := sub.keyFrom(fieldType)
+ // merge properties from sub
+ subModel, _ := sub.Definitions[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 {
+ if k == each {
+ required = true
+ break
+ }
+ }
+ if required {
+ model.Required = append(model.Required, k)
+ }
+ }
+ // add all new referenced models
+ for key, sub := range sub.Definitions {
+ if key != subKey {
+ if _, ok := b.Definitions[key]; !ok {
+ b.Definitions[key] = sub
+ }
+ }
+ }
+ // empty name signals skip property
+ return "", prop
+ }
+ // simple struct
+ b.addModel(fieldType, "")
+ var pType = b.keyFrom(fieldType)
+ prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
+ return jsonName, prop
+}
+
+func (b definitionBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
+ setPropertyMetadata(&prop, field)
+ fieldType := field.Type
+ if fieldType.Elem().Kind() == reflect.Uint8 {
+ stringt := "string"
+ prop.Type = []string{stringt}
+ return jsonName, prop
+ }
+ var pType = "array"
+ prop.Type = []string{pType}
+ isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
+ elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
+ prop.Items = &spec.SchemaOrArray{
+ Schema: &spec.Schema{},
+ }
+ if isPrimitive {
+ mapped := b.jsonSchemaType(elemTypeName)
+ prop.Items.Schema.Type = []string{mapped}
+ } else {
+ prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
+ }
+ // add|overwrite model for element type
+ if fieldType.Elem().Kind() == reflect.Ptr {
+ fieldType = fieldType.Elem()
+ }
+ if !isPrimitive {
+ b.addModel(fieldType.Elem(), elemTypeName)
+ }
+ return jsonName, prop
+}
+
+func (b definitionBuilder) buildMapTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
+ setPropertyMetadata(&prop, field)
+ fieldType := field.Type
+ var pType = "object"
+ prop.Type = []string{pType}
+
+ // As long as the element isn't an interface, we should be able to figure out what the
+ // intended type is and represent it in `AdditionalProperties`.
+ // See: https://swagger.io/docs/specification/data-models/dictionaries/
+ if fieldType.Elem().Kind().String() != "interface" {
+ isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
+ elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
+ prop.AdditionalProperties = &spec.SchemaOrBool{
+ Schema: &spec.Schema{},
+ }
+ if isPrimitive {
+ mapped := b.jsonSchemaType(elemTypeName)
+ prop.AdditionalProperties.Schema.Type = []string{mapped}
+ } else {
+ prop.AdditionalProperties.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemTypeName)
+ }
+ // add|overwrite model for element type
+ if fieldType.Elem().Kind() == reflect.Ptr {
+ fieldType = fieldType.Elem()
+ }
+ if !isPrimitive {
+ b.addModel(fieldType.Elem(), elemTypeName)
+ }
+ }
+ return jsonName, prop
+}
+
+func (b definitionBuilder) buildPointerTypeProperty(field reflect.StructField, jsonName, modelName string) (nameJson string, prop spec.Schema) {
+ setPropertyMetadata(&prop, field)
+ fieldType := field.Type
+
+ // override type of pointer to list-likes
+ if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
+ var pType = "array"
+ prop.Type = []string{pType}
+ isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name())
+ elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
+ prop.Items = &spec.SchemaOrArray{
+ Schema: &spec.Schema{},
+ }
+ if isPrimitive {
+ primName := b.jsonSchemaType(elemName)
+ prop.Items.Schema.Type = []string{primName}
+ } else {
+ prop.Items.Schema.Ref = spec.MustCreateRef("#/definitions/" + elemName)
+ }
+ if !isPrimitive {
+ // add|overwrite model for element type
+ b.addModel(fieldType.Elem().Elem(), elemName)
+ }
+ } else {
+ // non-array, pointer type
+ fieldTypeName := b.keyFrom(fieldType.Elem())
+ var pType = b.jsonSchemaType(fieldTypeName) // no star, include pkg path
+ if b.isPrimitiveType(fieldTypeName) {
+ prop.Type = []string{pType}
+ prop.Format = b.jsonSchemaFormat(fieldTypeName)
+ return jsonName, prop
+ }
+ prop.Ref = spec.MustCreateRef("#/definitions/" + pType)
+ elemName := ""
+ if fieldType.Elem().Name() == "" {
+ elemName = modelName + "." + jsonName
+ prop.Ref = spec.MustCreateRef("#/definitions/" + elemName)
+ }
+ b.addModel(fieldType.Elem(), elemName)
+ }
+ return jsonName, prop
+}
+
+func (b definitionBuilder) getElementTypeName(modelName, jsonName string, t reflect.Type) string {
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Name() == "" {
+ return modelName + "." + jsonName
+ }
+ return b.keyFrom(t)
+}
+
+func (b definitionBuilder) keyFrom(st reflect.Type) string {
+ key := st.String()
+ if b.Config.ModelTypeNameHandler != nil {
+ if name, ok := b.Config.ModelTypeNameHandler(st); ok {
+ key = name
+ }
+ }
+ if len(st.Name()) == 0 { // unnamed type
+ // If it is an array, remove the leading []
+ key = strings.TrimPrefix(key, "[]")
+ // Swagger UI has special meaning for [
+ key = strings.Replace(key, "[]", "||", -1)
+ }
+ return key
+}
+
+// see also https://golang.org/ref/spec#Numeric_types
+func (b definitionBuilder) isPrimitiveType(modelName string) bool {
+ if len(modelName) == 0 {
+ return false
+ }
+ return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
+}
+
+// jsonNameOfField returns the name of the field as it should appear in JSON format
+// An empty string indicates that this field is not part of the JSON representation
+func (b definitionBuilder) jsonNameOfField(field reflect.StructField) string {
+ if jsonTag := field.Tag.Get("json"); jsonTag != "" {
+ s := strings.Split(jsonTag, ",")
+ if s[0] == "-" {
+ // empty name signals skip property
+ return ""
+ } else if s[0] != "" {
+ return s[0]
+ }
+ }
+ return field.Name
+}
+
+// see also http://json-schema.org/latest/json-schema-core.html#anchor8
+func (b definitionBuilder) jsonSchemaType(modelName string) string {
+ schemaMap := map[string]string{
+ "uint": "integer",
+ "uint8": "integer",
+ "uint16": "integer",
+ "uint32": "integer",
+ "uint64": "integer",
+
+ "int": "integer",
+ "int8": "integer",
+ "int16": "integer",
+ "int32": "integer",
+ "int64": "integer",
+
+ "byte": "integer",
+ "float64": "number",
+ "float32": "number",
+ "bool": "boolean",
+ "time.Time": "string",
+ }
+ mapped, ok := schemaMap[modelName]
+ if !ok {
+ return modelName // use as is (custom or struct)
+ }
+ return mapped
+}
+
+func (b definitionBuilder) jsonSchemaFormat(modelName string) string {
+ if b.Config.SchemaFormatHandler != nil {
+ if mapped := b.Config.SchemaFormatHandler(modelName); mapped != "" {
+ return mapped
+ }
+ }
+ schemaMap := map[string]string{
+ "int": "int32",
+ "int32": "int32",
+ "int64": "int64",
+ "byte": "byte",
+ "uint": "integer",
+ "uint8": "byte",
+ "float64": "double",
+ "float32": "float",
+ "time.Time": "date-time",
+ "*time.Time": "date-time",
+ }
+ mapped, ok := schemaMap[modelName]
+ if !ok {
+ return "" // no format
+ }
+ return mapped
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/examples/security/api.go b/vendor/github.com/marccarre/go-restful-openapi/examples/security/api.go
new file mode 100644
index 000000000..fdbad1a24
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/examples/security/api.go
@@ -0,0 +1,107 @@
+package main
+
+import (
+ "strings"
+
+ "github.com/sirupsen/logrus"
+ restful "github.com/emicklei/go-restful"
+ "github.com/go-openapi/spec"
+)
+
+func enrichSwaggerObject(swo *spec.Swagger) {
+ swo.Info = &spec.Info{
+ InfoProps: spec.InfoProps{
+ Title: "Example",
+ Description: "Resource for doing example things",
+ Contact: &spec.ContactInfo{
+ Name: "dkiser",
+ Email: "domingo.kiser@gmail.com",
+ URL: "domingo.space",
+ },
+ License: &spec.License{
+ Name: "MIT",
+ URL: "http://mit.org",
+ },
+ Version: "1.0.0",
+ },
+ }
+ swo.Tags = []spec.Tag{spec.Tag{TagProps: spec.TagProps{
+ Name: "example",
+ Description: "Exampling and stuff"}}}
+
+ // setup security definitions
+ swo.SecurityDefinitions = map[string]*spec.SecurityScheme{
+ "jwt": spec.APIKeyAuth("Authorization", "header"),
+ }
+
+ // map routes to security definitions
+ enrichSwaggeerObjectSecurity(swo)
+}
+
+func enrichSwaggeerObjectSecurity(swo *spec.Swagger) {
+
+ // loop through all registerd web services
+ for _, ws := range restful.RegisteredWebServices() {
+ for _, route := range ws.Routes() {
+
+ // grab route metadata for a SecurityDefinition
+ secdefn, ok := route.Metadata[SecurityDefinitionKey]
+ if !ok {
+ continue
+ }
+
+ // grab pechelper.OAISecurity from the stored interface{}
+ var sEntry OAISecurity
+ switch v := secdefn.(type) {
+ case *OAISecurity:
+ sEntry = *v
+ case OAISecurity:
+ sEntry = v
+ default:
+ // not valid type
+ logrus.Warningf("skipping Security openapi spec for %s:%s, invalid metadata type %v", route.Method, route.Path, v)
+ continue
+ }
+
+ if _, ok := swo.SecurityDefinitions[sEntry.Name]; !ok {
+ logrus.Warningf("skipping Security openapi spec for %s:%s, '%s' not found in SecurityDefinitions", route.Method, route.Path, sEntry.Name)
+ continue
+ }
+
+ // grab path and path item in openapi spec
+ path, err := swo.Paths.JSONLookup(route.Path)
+ if err != nil {
+ logrus.Warning("skipping Security openapi spec for %s:%s, %s", route.Method, route.Path, err.Error())
+ continue
+ }
+ pItem := path.(*spec.PathItem)
+
+ // Update respective path Option based on method
+ var pOption *spec.Operation
+ switch method := strings.ToLower(route.Method); method {
+ case "get":
+ pOption = pItem.Get
+ case "post":
+ pOption = pItem.Post
+ case "patch":
+ pOption = pItem.Patch
+ case "delete":
+ pOption = pItem.Delete
+ case "put":
+ pOption = pItem.Put
+ case "head":
+ pOption = pItem.Head
+ case "options":
+ pOption = pItem.Options
+ default:
+ // unsupported method
+ logrus.Warningf("skipping Security openapi spec for %s:%s, unsupported method '%s'", route.Method, route.Path, route.Method)
+ continue
+ }
+
+ // update the pOption with security entry
+ pOption.SecuredWith(sEntry.Name, sEntry.Scopes...)
+ }
+ }
+
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/examples/security/main.go b/vendor/github.com/marccarre/go-restful-openapi/examples/security/main.go
new file mode 100644
index 000000000..55110a251
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/examples/security/main.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "encoding/json"
+ "os"
+
+ restful "github.com/emicklei/go-restful"
+ restfulspec "github.com/emicklei/go-restful-openapi"
+)
+
+func main() {
+ config := restfulspec.Config{
+ WebServices: restful.RegisteredWebServices(), // you control what services are visible
+ APIPath: "/apidocs.json",
+ PostBuildSwaggerObjectHandler: enrichSwaggerObject}
+ swagger := restfulspec.BuildSwagger(config)
+ enc := json.NewEncoder(os.Stdout)
+ enc.SetIndent("", "\t")
+ enc.Encode(swagger)
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/examples/security/service.go b/vendor/github.com/marccarre/go-restful-openapi/examples/security/service.go
new file mode 100644
index 000000000..8cdebea6e
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/examples/security/service.go
@@ -0,0 +1,33 @@
+package main
+
+import restful "github.com/emicklei/go-restful"
+import restfulspec "github.com/emicklei/go-restful-openapi"
+
+type ExampleService struct{}
+type ExampleReq struct{}
+type ExampleResp struct{}
+type ResponseMsg struct{}
+
+func (s ExampleService) WebService() *restful.WebService {
+ ws := new(restful.WebService)
+ ws.
+ Path("/example").
+ Consumes(restful.MIME_JSON).
+ Produces(restful.MIME_JSON)
+
+ tags := []string{"example"}
+
+ ws.Route(ws.POST("/example").To(s.create).
+ Doc("create example thing").
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Metadata(SecurityDefinitionKey, OAISecurity{Name: "jwt"}).
+ Writes(ExampleResp{}).
+ Reads(ExampleReq{}).
+ Returns(200, "OK", &ExampleResp{}).
+ Returns(404, "NotFound", &ResponseMsg{}).
+ Returns(500, "InternalServerError", &ResponseMsg{}))
+
+ return ws
+
+}
+func (s ExampleService) create(*restful.Request, *restful.Response) {}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/examples/security/spechelper.go b/vendor/github.com/marccarre/go-restful-openapi/examples/security/spechelper.go
new file mode 100644
index 000000000..1660bf49d
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/examples/security/spechelper.go
@@ -0,0 +1,27 @@
+package main
+
+import "fmt"
+
+const (
+ SecurityDefinitionKey = "OAPI_SECURITY_DEFINITION"
+)
+
+type OAISecurity struct {
+ Name string // SecurityDefinition name
+ Scopes []string // Scopes for oauth2
+}
+
+func (s *OAISecurity) Valid() error {
+ switch s.Name {
+ case "oauth2":
+ return nil
+ case "openIdConnect":
+ return nil
+ default:
+ if len(s.Scopes) > 0 {
+ return fmt.Errorf("oai Security scopes for scheme '%s' should be empty", s.Name)
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/examples/user-resource.go b/vendor/github.com/marccarre/go-restful-openapi/examples/user-resource.go
new file mode 100644
index 000000000..b5203acf4
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/examples/user-resource.go
@@ -0,0 +1,176 @@
+package main
+
+import (
+ "log"
+ "net/http"
+
+ "github.com/emicklei/go-restful"
+ restfulspec "github.com/emicklei/go-restful-openapi"
+ "github.com/go-openapi/spec"
+)
+
+type UserResource struct {
+ // normally one would use DAO (data access object)
+ users map[string]User
+}
+
+func (u UserResource) WebService() *restful.WebService {
+ ws := new(restful.WebService)
+ ws.
+ Path("/users").
+ Consumes(restful.MIME_XML, restful.MIME_JSON).
+ Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well
+
+ tags := []string{"users"}
+
+ ws.Route(ws.GET("/").To(u.findAllUsers).
+ // docs
+ Doc("get all users").
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Writes([]User{}).
+ Returns(200, "OK", []User{}))
+
+ ws.Route(ws.GET("/{user-id}").To(u.findUser).
+ // docs
+ Doc("get a user").
+ Param(ws.PathParameter("user-id", "identifier of the user").DataType("integer").DefaultValue("1")).
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Writes(User{}). // on the response
+ Returns(200, "OK", User{}).
+ Returns(404, "Not Found", nil))
+
+ ws.Route(ws.PUT("/{user-id}").To(u.updateUser).
+ // docs
+ Doc("update a user").
+ Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")).
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Reads(User{})) // from the request
+
+ ws.Route(ws.PUT("").To(u.createUser).
+ // docs
+ Doc("create a user").
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Reads(User{})) // from the request
+
+ ws.Route(ws.DELETE("/{user-id}").To(u.removeUser).
+ // docs
+ Doc("delete a user").
+ Metadata(restfulspec.KeyOpenAPITags, tags).
+ Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")))
+
+ return ws
+}
+
+// GET http://localhost:8080/users
+//
+func (u UserResource) findAllUsers(request *restful.Request, response *restful.Response) {
+ list := []User{}
+ for _, each := range u.users {
+ list = append(list, each)
+ }
+ response.WriteEntity(list)
+}
+
+// GET http://localhost:8080/users/1
+//
+func (u UserResource) findUser(request *restful.Request, response *restful.Response) {
+ id := request.PathParameter("user-id")
+ usr := u.users[id]
+ if len(usr.ID) == 0 {
+ response.WriteErrorString(http.StatusNotFound, "User could not be found.")
+ } else {
+ response.WriteEntity(usr)
+ }
+}
+
+// PUT http://localhost:8080/users/1
+// 1Melissa Raspberry
+//
+func (u *UserResource) updateUser(request *restful.Request, response *restful.Response) {
+ usr := new(User)
+ err := request.ReadEntity(&usr)
+ if err == nil {
+ u.users[usr.ID] = *usr
+ response.WriteEntity(usr)
+ } else {
+ response.WriteError(http.StatusInternalServerError, err)
+ }
+}
+
+// PUT http://localhost:8080/users/1
+// 1Melissa
+//
+func (u *UserResource) createUser(request *restful.Request, response *restful.Response) {
+ usr := User{ID: request.PathParameter("user-id")}
+ err := request.ReadEntity(&usr)
+ if err == nil {
+ u.users[usr.ID] = usr
+ response.WriteHeaderAndEntity(http.StatusCreated, usr)
+ } else {
+ response.WriteError(http.StatusInternalServerError, err)
+ }
+}
+
+// DELETE http://localhost:8080/users/1
+//
+func (u *UserResource) removeUser(request *restful.Request, response *restful.Response) {
+ id := request.PathParameter("user-id")
+ delete(u.users, id)
+}
+
+func main() {
+ u := UserResource{map[string]User{}}
+ restful.DefaultContainer.Add(u.WebService())
+
+ config := restfulspec.Config{
+ WebServices: restful.RegisteredWebServices(), // you control what services are visible
+ APIPath: "/apidocs.json",
+ PostBuildSwaggerObjectHandler: enrichSwaggerObject}
+ restful.DefaultContainer.Add(restfulspec.NewOpenAPIService(config))
+
+ // Optionally, 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/?url=http://localhost:8080/apidocs.json
+ http.Handle("/apidocs/", http.StripPrefix("/apidocs/", http.FileServer(http.Dir("/Users/emicklei/Projects/swagger-ui/dist"))))
+
+ // Optionally, you may need to enable CORS for the UI to work.
+ cors := restful.CrossOriginResourceSharing{
+ AllowedHeaders: []string{"Content-Type", "Accept"},
+ AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
+ CookiesAllowed: false,
+ Container: restful.DefaultContainer}
+ restful.DefaultContainer.Filter(cors.Filter)
+
+ log.Printf("Get the API using http://localhost:8080/apidocs.json")
+ log.Printf("Open Swagger UI using http://localhost:8080/apidocs/?url=http://localhost:8080/apidocs.json")
+ log.Fatal(http.ListenAndServe(":8080", nil))
+}
+
+func enrichSwaggerObject(swo *spec.Swagger) {
+ swo.Info = &spec.Info{
+ InfoProps: spec.InfoProps{
+ Title: "UserService",
+ Description: "Resource for managing Users",
+ Contact: &spec.ContactInfo{
+ Name: "john",
+ Email: "john@doe.rp",
+ URL: "http://johndoe.org",
+ },
+ License: &spec.License{
+ Name: "MIT",
+ URL: "http://mit.org",
+ },
+ Version: "1.0.0",
+ },
+ }
+ swo.Tags = []spec.Tag{spec.Tag{TagProps: spec.TagProps{
+ Name: "users",
+ Description: "Managing users"}}}
+}
+
+// User is just a sample type
+type User struct {
+ ID string `json:"id" description:"identifier of the user"`
+ Name string `json:"name" description:"name of the user" default:"john"`
+ Age int `json:"age" description:"age of the user" default:"21"`
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/lookup.go b/vendor/github.com/marccarre/go-restful-openapi/lookup.go
new file mode 100644
index 000000000..27407107a
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/lookup.go
@@ -0,0 +1,19 @@
+package restfulspec
+
+import restful "github.com/emicklei/go-restful"
+
+func asParamType(kind int) string {
+ switch {
+ case kind == restful.PathParameterKind:
+ return "path"
+ case kind == restful.QueryParameterKind:
+ return "query"
+ case kind == restful.BodyParameterKind:
+ return "body"
+ case kind == restful.HeaderParameterKind:
+ return "header"
+ case kind == restful.FormParameterKind:
+ return "formData"
+ }
+ return ""
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/property_ext.go b/vendor/github.com/marccarre/go-restful-openapi/property_ext.go
new file mode 100644
index 000000000..eb91208bc
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/property_ext.go
@@ -0,0 +1,104 @@
+package restfulspec
+
+import (
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/go-openapi/spec"
+)
+
+func setDescription(prop *spec.Schema, field reflect.StructField) {
+ if tag := field.Tag.Get("description"); tag != "" {
+ prop.Description = tag
+ }
+}
+
+func setDefaultValue(prop *spec.Schema, field reflect.StructField) {
+ if tag := field.Tag.Get("default"); tag != "" {
+ prop.Default = stringAutoType(tag)
+ }
+}
+
+func setEnumValues(prop *spec.Schema, 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 != "" {
+ enums := []interface{}{}
+ for _, s := range strings.Split(tag, "|") {
+ enums = append(enums, s)
+ }
+ prop.Enum = enums
+ }
+}
+
+func setMaximum(prop *spec.Schema, field reflect.StructField) {
+ if tag := field.Tag.Get("maximum"); tag != "" {
+ value, err := strconv.ParseFloat(tag, 64)
+ if err == nil {
+ prop.Maximum = &value
+ }
+ }
+}
+
+func setMinimum(prop *spec.Schema, field reflect.StructField) {
+ if tag := field.Tag.Get("minimum"); tag != "" {
+ value, err := strconv.ParseFloat(tag, 64)
+ if err == nil {
+ prop.Minimum = &value
+ }
+ }
+}
+
+func setType(prop *spec.Schema, field reflect.StructField) {
+ if tag := field.Tag.Get("type"); tag != "" {
+ // Check if the first two characters of the type tag are
+ // intended to emulate slice/array behaviour.
+ //
+ // If type is intended to be a slice/array then add the
+ // overriden type to the array item instead of the main property
+ if len(tag) > 2 && tag[0:2] == "[]" {
+ pType := "array"
+ prop.Type = []string{pType}
+ prop.Items = &spec.SchemaOrArray{
+ Schema: &spec.Schema{},
+ }
+ iType := tag[2:]
+ prop.Items.Schema.Type = []string{iType}
+ return
+ }
+
+ prop.Type = []string{tag}
+ }
+}
+
+func setUniqueItems(prop *spec.Schema, field reflect.StructField) {
+ tag := field.Tag.Get("unique")
+ switch tag {
+ case "true":
+ prop.UniqueItems = true
+ case "false":
+ prop.UniqueItems = false
+ }
+}
+
+func setReadOnly(prop *spec.Schema, field reflect.StructField) {
+ tag := field.Tag.Get("readOnly")
+ switch tag {
+ case "true":
+ prop.ReadOnly = true
+ case "false":
+ prop.ReadOnly = false
+ }
+}
+
+func setPropertyMetadata(prop *spec.Schema, field reflect.StructField) {
+ setDescription(prop, field)
+ setDefaultValue(prop, field)
+ setEnumValues(prop, field)
+ setMinimum(prop, field)
+ setMaximum(prop, field)
+ setUniqueItems(prop, field)
+ setType(prop, field)
+ setReadOnly(prop, field)
+}
diff --git a/vendor/github.com/marccarre/go-restful-openapi/spec_resource.go b/vendor/github.com/marccarre/go-restful-openapi/spec_resource.go
new file mode 100644
index 000000000..b36274061
--- /dev/null
+++ b/vendor/github.com/marccarre/go-restful-openapi/spec_resource.go
@@ -0,0 +1,76 @@
+package restfulspec
+
+import (
+ restful "github.com/emicklei/go-restful"
+ "github.com/go-openapi/spec"
+)
+
+// NewOpenAPIService returns a new WebService that provides the API documentation of all services
+// conform the OpenAPI documentation specifcation.
+func NewOpenAPIService(config Config) *restful.WebService {
+
+ ws := new(restful.WebService)
+ ws.Path(config.APIPath)
+ ws.Produces(restful.MIME_JSON)
+ if config.DisableCORS {
+ ws.Filter(enableCORS)
+ }
+
+ swagger := BuildSwagger(config)
+ resource := specResource{swagger: swagger}
+ ws.Route(ws.GET("/").To(resource.getSwagger))
+ return ws
+}
+
+// BuildSwagger returns a Swagger object for all services' API endpoints.
+func BuildSwagger(config Config) *spec.Swagger {
+ // collect paths and model definitions to build Swagger object.
+ paths := &spec.Paths{Paths: map[string]spec.PathItem{}}
+ definitions := spec.Definitions{}
+
+ for _, each := range config.WebServices {
+ for path, item := range buildPaths(each, config).Paths {
+ existingPathItem, ok := paths.Paths[path]
+ if ok {
+ for _, r := range each.Routes() {
+ _, patterns := sanitizePath(r.Path)
+ item = buildPathItem(each, r, existingPathItem, patterns, config)
+ }
+ }
+ paths.Paths[path] = item
+ }
+ for name, def := range buildDefinitions(each, config) {
+ definitions[name] = def
+ }
+ }
+ swagger := &spec.Swagger{
+ SwaggerProps: spec.SwaggerProps{
+ Swagger: "2.0",
+ Paths: paths,
+ Definitions: definitions,
+ },
+ }
+ if config.PostBuildSwaggerObjectHandler != nil {
+ config.PostBuildSwaggerObjectHandler(swagger)
+ }
+ return swagger
+}
+
+func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
+ if origin := req.HeaderParameter(restful.HEADER_Origin); origin != "" {
+ // prevent duplicate header
+ if len(resp.Header().Get(restful.HEADER_AccessControlAllowOrigin)) == 0 {
+ resp.AddHeader(restful.HEADER_AccessControlAllowOrigin, origin)
+ }
+ }
+ chain.ProcessFilter(req, resp)
+}
+
+// specResource is a REST resource to serve the Open-API spec.
+type specResource struct {
+ swagger *spec.Swagger
+}
+
+func (s specResource) getSwagger(req *restful.Request, resp *restful.Response) {
+ resp.WriteAsJson(s.swagger)
+}
diff --git a/vendor/manifest b/vendor/manifest
index 9d7b77a12..b47188f09 100644
--- a/vendor/manifest
+++ b/vendor/manifest
@@ -57,6 +57,14 @@
"branch": "master",
"notests": true
},
+ {
+ "importpath": "github.com/Sirupsen/logrus",
+ "repository": "https://github.com/Sirupsen/logrus",
+ "vcs": "git",
+ "revision": "c108f5553c369120b773da633aca62a8b42b1cf4",
+ "branch": "master",
+ "notests": true
+ },
{
"importpath": "github.com/VividCortex/gohistogram",
"repository": "https://github.com/VividCortex/gohistogram",
@@ -552,7 +560,7 @@
"importpath": "github.com/emicklei/go-restful-openapi",
"repository": "https://github.com/emicklei/go-restful-openapi",
"vcs": "git",
- "revision": "0b23be9885ff0876fdf5dc133e5c9def4a7ab3ec",
+ "revision": "ca11c31130f54bf8dfaa69dac83d8603b2d4b48e",
"branch": "master",
"notests": true
},
@@ -1157,6 +1165,14 @@
"path": "/jwriter",
"notests": true
},
+ {
+ "importpath": "github.com/marccarre/go-restful-openapi",
+ "repository": "https://github.com/marccarre/go-restful-openapi",
+ "vcs": "git",
+ "revision": "129557de7d9f2d2ca4a90cd31c379db90a561ad8",
+ "branch": "HEAD",
+ "notests": true
+ },
{
"importpath": "github.com/mattn/go-colorable",
"repository": "https://github.com/mattn/go-colorable",