refactor to go-restful
This commit is contained in:
1
vendor/github.com/emicklei/go-restful-openapi/.gitignore
generated
vendored
Normal file
1
vendor/github.com/emicklei/go-restful-openapi/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
examples/examples
|
4
vendor/github.com/emicklei/go-restful-openapi/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/emicklei/go-restful-openapi/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
16
vendor/github.com/emicklei/go-restful-openapi/CHANGES.md
generated
vendored
Normal file
16
vendor/github.com/emicklei/go-restful-openapi/CHANGES.md
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# changes to the go-restful-openapi package
|
||||
|
||||
## v1.0.0
|
||||
|
||||
- Fix for #19 MapModelTypeNameFunc has incomplete behavior
|
||||
- prevent array param.Type be overwritten in the else case below (#47)
|
||||
- Merge paths with existing paths from other webServices (#48)
|
||||
|
||||
## v0.11.0
|
||||
|
||||
- Register pointer to array/slice of primitives as such rather than as reference to the primitive type definition. (#46)
|
||||
- Add support for map types using "additional properties" (#44)
|
||||
|
||||
## <= v0.10.0
|
||||
|
||||
See `git log`.
|
22
vendor/github.com/emicklei/go-restful-openapi/LICENSE
generated
vendored
Normal file
22
vendor/github.com/emicklei/go-restful-openapi/LICENSE
generated
vendored
Normal file
@ -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.
|
26
vendor/github.com/emicklei/go-restful-openapi/README.md
generated
vendored
Normal file
26
vendor/github.com/emicklei/go-restful-openapi/README.md
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# go-restful-openapi
|
||||
|
||||
[](https://travis-ci.org/emicklei/go-restful-openapi)
|
||||
[](https://godoc.org/github.com/emicklei/go-restful-openapi)
|
||||
|
||||
[openapi](https://www.openapis.org) extension to the go-restful package, targeting [version 2.0](https://github.com/OAI/OpenAPI-Specification)
|
||||
|
||||
## The following Go field tags are translated to OpenAPI equivalents
|
||||
- description
|
||||
- minimum
|
||||
- maximum
|
||||
- optional ( if set to "true" then it is not listed in `required`)
|
||||
- unique
|
||||
- modelDescription
|
||||
- type (overrides the Go type String())
|
||||
- enum
|
||||
- readOnly
|
||||
|
||||
See TestThatExtraTagsAreReadIntoModel for examples.
|
||||
|
||||
## dependencies
|
||||
|
||||
- [go-restful](https://github.com/emicklei/go-restful)
|
||||
- [go-openapi](https://github.com/go-openapi/spec)
|
||||
|
||||
© 2018, ernestmicklei.com. MIT License. Contributions welcome.
|
32
vendor/github.com/emicklei/go-restful-openapi/build_definitions.go
generated
vendored
Normal file
32
vendor/github.com/emicklei/go-restful-openapi/build_definitions.go
generated
vendored
Normal file
@ -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), "")
|
||||
}
|
||||
}
|
254
vendor/github.com/emicklei/go-restful-openapi/build_path.go
generated
vendored
Normal file
254
vendor/github.com/emicklei/go-restful-openapi/build_path.go
generated
vendored
Normal file
@ -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)
|
||||
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 := keyFrom(st.Elem(), cfg)
|
||||
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 {
|
||||
dataTypeName := keyFrom(st, cfg)
|
||||
p.Schema.Ref = spec.MustCreateRef("#/definitions/" + dataTypeName)
|
||||
}
|
||||
|
||||
} else {
|
||||
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.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 := keyFrom(st.Elem(), cfg)
|
||||
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 := keyFrom(st, cfg)
|
||||
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 := keyFrom(st, cfg)
|
||||
r.Schema.Ref = spec.MustCreateRef("#/definitions/" + modelName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// stripTags takes a snippet of HTML and returns only the text content.
|
||||
// For example, `<b><Hi!></b> <br>` -> `<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
|
||||
}
|
41
vendor/github.com/emicklei/go-restful-openapi/config.go
generated
vendored
Normal file
41
vendor/github.com/emicklei/go-restful-openapi/config.go
generated
vendored
Normal file
@ -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
|
||||
}
|
493
vendor/github.com/emicklei/go-restful-openapi/definition_builder.go
generated
vendored
Normal file
493
vendor/github.com/emicklei/go-restful-openapi/definition_builder.go
generated
vendored
Normal file
@ -0,0 +1,493 @@
|
||||
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 (<theModel>) 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 := keyFrom(st, b.Config)
|
||||
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(keyFrom(fieldType, b.Config))
|
||||
}
|
||||
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 := keyFrom(fieldType, b.Config)
|
||||
if b.isPrimitiveType(fieldTypeName) {
|
||||
mapped := b.jsonSchemaType(fieldTypeName)
|
||||
prop.Type = []string{mapped}
|
||||
prop.Format = b.jsonSchemaFormat(fieldTypeName)
|
||||
return jsonName, modelDescription, prop
|
||||
}
|
||||
modelType := keyFrom(fieldType, b.Config)
|
||||
prop.Ref = spec.MustCreateRef("#/definitions/" + modelType)
|
||||
|
||||
if fieldType.Name() == "" { // override type of anonymous structs
|
||||
// FIXME: Still need a way to handle anonymous struct model naming.
|
||||
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
|
||||
// FIXME: Still need a way to handle anonymous struct model naming.
|
||||
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 := keyFrom(fieldType, b.Config)
|
||||
// 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 = keyFrom(fieldType, b.Config)
|
||||
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 := keyFrom(fieldType.Elem(), b.Config)
|
||||
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 keyFrom(t, b.Config)
|
||||
}
|
||||
|
||||
func keyFrom(st reflect.Type, cfg Config) string {
|
||||
key := st.String()
|
||||
if cfg.ModelTypeNameHandler != nil {
|
||||
if name, ok := cfg.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
|
||||
}
|
19
vendor/github.com/emicklei/go-restful-openapi/lookup.go
generated
vendored
Normal file
19
vendor/github.com/emicklei/go-restful-openapi/lookup.go
generated
vendored
Normal file
@ -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 ""
|
||||
}
|
104
vendor/github.com/emicklei/go-restful-openapi/property_ext.go
generated
vendored
Normal file
104
vendor/github.com/emicklei/go-restful-openapi/property_ext.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
76
vendor/github.com/emicklei/go-restful-openapi/spec_resource.go
generated
vendored
Normal file
76
vendor/github.com/emicklei/go-restful-openapi/spec_resource.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
Reference in New Issue
Block a user