757 lines
19 KiB
Go
757 lines
19 KiB
Go
package analysis
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"path"
|
|
"sort"
|
|
"strings"
|
|
|
|
"strconv"
|
|
|
|
"github.com/go-openapi/jsonpointer"
|
|
swspec "github.com/go-openapi/spec"
|
|
"github.com/go-openapi/swag"
|
|
)
|
|
|
|
// FlattenOpts configuration for flattening a swagger specification.
|
|
type FlattenOpts struct {
|
|
Spec *Spec
|
|
BasePath string
|
|
|
|
_ struct{} // require keys
|
|
}
|
|
|
|
// ExpandOpts creates a spec.ExpandOptions to configure expanding a specification document.
|
|
func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *swspec.ExpandOptions {
|
|
return &swspec.ExpandOptions{RelativeBase: f.BasePath, SkipSchemas: skipSchemas}
|
|
}
|
|
|
|
// Swagger gets the swagger specification for this flatten operation
|
|
func (f *FlattenOpts) Swagger() *swspec.Swagger {
|
|
return f.Spec.spec
|
|
}
|
|
|
|
// Flatten an analyzed spec.
|
|
//
|
|
// To flatten a spec means:
|
|
//
|
|
// Expand the parameters, responses, path items, parameter items and header items.
|
|
// Import external (http, file) references so they become internal to the document.
|
|
// Move every inline schema to be a definition with an auto-generated name in a depth-first fashion.
|
|
// Rewritten schemas get a vendor extension x-go-gen-location so we know in which package they need to be rendered.
|
|
func Flatten(opts FlattenOpts) error {
|
|
// recursively expand responses, parameters, path items and items
|
|
err := swspec.ExpandSpec(opts.Swagger(), opts.ExpandOpts(true))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
opts.Spec.reload() // re-analyze
|
|
|
|
// at this point there are no other references left but schemas
|
|
if err := importExternalReferences(&opts); err != nil {
|
|
return err
|
|
}
|
|
opts.Spec.reload() // re-analyze
|
|
|
|
// rewrite the inline schemas (schemas that aren't simple types or arrays of simple types)
|
|
if err := nameInlinedSchemas(&opts); err != nil {
|
|
return err
|
|
}
|
|
opts.Spec.reload() // re-analyze
|
|
|
|
// TODO: simplifiy known schema patterns to flat objects with properties?
|
|
return nil
|
|
}
|
|
|
|
func nameInlinedSchemas(opts *FlattenOpts) error {
|
|
namer := &inlineSchemaNamer{Spec: opts.Swagger(), Operations: opRefsByRef(gatherOperations(opts.Spec, nil))}
|
|
depthFirst := sortDepthFirst(opts.Spec.allSchemas)
|
|
|
|
for _, key := range depthFirst {
|
|
sch := opts.Spec.allSchemas[key]
|
|
if sch.Schema != nil && sch.Schema.Ref.String() == "" && !sch.TopLevel { // inline schema
|
|
asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath})
|
|
if err != nil {
|
|
return fmt.Errorf("schema analysis [%s]: %v", sch.Ref.String(), err)
|
|
}
|
|
|
|
if !asch.IsSimpleSchema { // complex schemas get moved
|
|
if err := namer.Name(key, sch.Schema, asch); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var depthGroupOrder = []string{"sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition"}
|
|
|
|
func sortDepthFirst(data map[string]SchemaRef) (sorted []string) {
|
|
// group by category (shared params, op param, statuscode response, default response, definitions)
|
|
// sort groups internally by number of parts in the key and lexical names
|
|
// flatten groups into a single list of keys
|
|
grouped := make(map[string]keys, len(data))
|
|
for k := range data {
|
|
split := keyParts(k)
|
|
var pk string
|
|
if split.IsSharedOperationParam() {
|
|
pk = "sharedOpParam"
|
|
}
|
|
if split.IsOperationParam() {
|
|
pk = "opParam"
|
|
}
|
|
if split.IsStatusCodeResponse() {
|
|
pk = "codeResponse"
|
|
}
|
|
if split.IsDefaultResponse() {
|
|
pk = "defaultResponse"
|
|
}
|
|
if split.IsDefinition() {
|
|
pk = "definition"
|
|
}
|
|
grouped[pk] = append(grouped[pk], key{len(split), k})
|
|
}
|
|
|
|
for _, pk := range depthGroupOrder {
|
|
res := grouped[pk]
|
|
sort.Sort(res)
|
|
for _, v := range res {
|
|
sorted = append(sorted, v.Key)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
type key struct {
|
|
Segments int
|
|
Key string
|
|
}
|
|
type keys []key
|
|
|
|
func (k keys) Len() int { return len(k) }
|
|
func (k keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
|
|
func (k keys) Less(i, j int) bool {
|
|
return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key)
|
|
}
|
|
|
|
type inlineSchemaNamer struct {
|
|
Spec *swspec.Swagger
|
|
Operations map[string]opRef
|
|
}
|
|
|
|
func opRefsByRef(oprefs map[string]opRef) map[string]opRef {
|
|
result := make(map[string]opRef, len(oprefs))
|
|
for _, v := range oprefs {
|
|
result[v.Ref.String()] = v
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (isn *inlineSchemaNamer) Name(key string, schema *swspec.Schema, aschema *AnalyzedSchema) error {
|
|
if swspec.Debug {
|
|
log.Printf("naming inlined schema at %s", key)
|
|
}
|
|
|
|
parts := keyParts(key)
|
|
for _, name := range namesFromKey(parts, aschema, isn.Operations) {
|
|
if name != "" {
|
|
// create unique name
|
|
newName := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name))
|
|
|
|
// clone schema
|
|
sch, err := cloneSchema(schema)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// replace values on schema
|
|
if err := rewriteSchemaToRef(isn.Spec, key, swspec.MustCreateRef("#/definitions/"+newName)); err != nil {
|
|
return fmt.Errorf("name inlined schema: %v", err)
|
|
}
|
|
|
|
sch.AddExtension("x-go-gen-location", genLocation(parts))
|
|
// fmt.Printf("{\n %q,\n \"\",\n spec.MustCreateRef(%q),\n \"\",\n},\n", key, "#/definitions/"+newName)
|
|
// save cloned schema to definitions
|
|
saveSchema(isn.Spec, newName, sch)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func genLocation(parts splitKey) string {
|
|
if parts.IsOperation() {
|
|
return "operations"
|
|
}
|
|
if parts.IsDefinition() {
|
|
return "models"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func uniqifyName(definitions swspec.Definitions, name string) string {
|
|
if name == "" {
|
|
name = "oaiGen"
|
|
}
|
|
if len(definitions) == 0 {
|
|
return name
|
|
}
|
|
|
|
if _, ok := definitions[name]; !ok {
|
|
return name
|
|
}
|
|
name += "OAIGen"
|
|
var idx int
|
|
unique := name
|
|
_, known := definitions[unique]
|
|
for known {
|
|
idx++
|
|
unique = fmt.Sprintf("%s%d", name, idx)
|
|
_, known = definitions[unique]
|
|
}
|
|
return unique
|
|
}
|
|
|
|
func namesFromKey(parts splitKey, aschema *AnalyzedSchema, operations map[string]opRef) []string {
|
|
var baseNames [][]string
|
|
var startIndex int
|
|
if parts.IsOperation() {
|
|
// params
|
|
if parts.IsOperationParam() || parts.IsSharedOperationParam() {
|
|
piref := parts.PathItemRef()
|
|
if piref.String() != "" && parts.IsOperationParam() {
|
|
if op, ok := operations[piref.String()]; ok {
|
|
startIndex = 5
|
|
baseNames = append(baseNames, []string{op.ID, "params", "body"})
|
|
}
|
|
} else if parts.IsSharedOperationParam() {
|
|
pref := parts.PathRef()
|
|
for k, v := range operations {
|
|
if strings.HasPrefix(k, pref.String()) {
|
|
startIndex = 4
|
|
baseNames = append(baseNames, []string{v.ID, "params", "body"})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// responses
|
|
if parts.IsOperationResponse() {
|
|
piref := parts.PathItemRef()
|
|
if piref.String() != "" {
|
|
if op, ok := operations[piref.String()]; ok {
|
|
startIndex = 6
|
|
baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// definitions
|
|
if parts.IsDefinition() {
|
|
nm := parts.DefinitionName()
|
|
if nm != "" {
|
|
startIndex = 2
|
|
baseNames = append(baseNames, []string{parts.DefinitionName()})
|
|
}
|
|
}
|
|
|
|
var result []string
|
|
for _, segments := range baseNames {
|
|
nm := parts.BuildName(segments, startIndex, aschema)
|
|
if nm != "" {
|
|
result = append(result, nm)
|
|
}
|
|
}
|
|
sort.Strings(result)
|
|
return result
|
|
}
|
|
|
|
const (
|
|
pths = "paths"
|
|
responses = "responses"
|
|
parameters = "parameters"
|
|
definitions = "definitions"
|
|
)
|
|
|
|
var ignoredKeys map[string]struct{}
|
|
|
|
func init() {
|
|
ignoredKeys = map[string]struct{}{
|
|
"schema": {},
|
|
"properties": {},
|
|
"not": {},
|
|
"anyOf": {},
|
|
"oneOf": {},
|
|
}
|
|
}
|
|
|
|
type splitKey []string
|
|
|
|
func (s splitKey) IsDefinition() bool {
|
|
return len(s) > 1 && s[0] == definitions
|
|
}
|
|
|
|
func (s splitKey) DefinitionName() string {
|
|
if !s.IsDefinition() {
|
|
return ""
|
|
}
|
|
return s[1]
|
|
}
|
|
|
|
func (s splitKey) BuildName(segments []string, startIndex int, aschema *AnalyzedSchema) string {
|
|
for _, part := range s[startIndex:] {
|
|
if _, ignored := ignoredKeys[part]; !ignored {
|
|
if part == "items" || part == "additionalItems" {
|
|
if aschema.IsTuple || aschema.IsTupleWithExtra {
|
|
segments = append(segments, "tuple")
|
|
} else {
|
|
segments = append(segments, "items")
|
|
}
|
|
if part == "additionalItems" {
|
|
segments = append(segments, part)
|
|
}
|
|
continue
|
|
}
|
|
segments = append(segments, part)
|
|
}
|
|
}
|
|
return strings.Join(segments, " ")
|
|
}
|
|
|
|
func (s splitKey) IsOperation() bool {
|
|
return len(s) > 1 && s[0] == pths
|
|
}
|
|
|
|
func (s splitKey) IsSharedOperationParam() bool {
|
|
return len(s) > 2 && s[0] == pths && s[2] == parameters
|
|
}
|
|
|
|
func (s splitKey) IsOperationParam() bool {
|
|
return len(s) > 3 && s[0] == pths && s[3] == parameters
|
|
}
|
|
|
|
func (s splitKey) IsOperationResponse() bool {
|
|
return len(s) > 3 && s[0] == pths && s[3] == responses
|
|
}
|
|
|
|
func (s splitKey) IsDefaultResponse() bool {
|
|
return len(s) > 4 && s[0] == pths && s[3] == responses && s[4] == "default"
|
|
}
|
|
|
|
func (s splitKey) IsStatusCodeResponse() bool {
|
|
isInt := func() bool {
|
|
_, err := strconv.Atoi(s[4])
|
|
return err == nil
|
|
}
|
|
return len(s) > 4 && s[0] == pths && s[3] == responses && isInt()
|
|
}
|
|
|
|
func (s splitKey) ResponseName() string {
|
|
if s.IsStatusCodeResponse() {
|
|
code, _ := strconv.Atoi(s[4])
|
|
return http.StatusText(code)
|
|
}
|
|
if s.IsDefaultResponse() {
|
|
return "Default"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
var validMethods map[string]struct{}
|
|
|
|
func init() {
|
|
validMethods = map[string]struct{}{
|
|
"GET": {},
|
|
"HEAD": {},
|
|
"OPTIONS": {},
|
|
"PATCH": {},
|
|
"POST": {},
|
|
"PUT": {},
|
|
"DELETE": {},
|
|
}
|
|
}
|
|
|
|
func (s splitKey) PathItemRef() swspec.Ref {
|
|
if len(s) < 3 {
|
|
return swspec.Ref{}
|
|
}
|
|
pth, method := s[1], s[2]
|
|
if _, validMethod := validMethods[strings.ToUpper(method)]; !validMethod && !strings.HasPrefix(method, "x-") {
|
|
return swspec.Ref{}
|
|
}
|
|
return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(pth), strings.ToUpper(method)))
|
|
}
|
|
|
|
func (s splitKey) PathRef() swspec.Ref {
|
|
if !s.IsOperation() {
|
|
return swspec.Ref{}
|
|
}
|
|
return swspec.MustCreateRef("#" + path.Join("/", pths, jsonpointer.Escape(s[1])))
|
|
}
|
|
|
|
func keyParts(key string) splitKey {
|
|
var res []string
|
|
for _, part := range strings.Split(key[1:], "/") {
|
|
if part != "" {
|
|
res = append(res, jsonpointer.Unescape(part))
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func rewriteSchemaToRef(spec *swspec.Swagger, key string, ref swspec.Ref) error {
|
|
if swspec.Debug {
|
|
log.Printf("rewriting schema to ref for %s with %s", key, ref.String())
|
|
}
|
|
pth := key[1:]
|
|
ptr, err := jsonpointer.New(pth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
value, _, err := ptr.Get(spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch refable := value.(type) {
|
|
case *swspec.Schema:
|
|
return rewriteParentRef(spec, key, ref)
|
|
case *swspec.SchemaOrBool:
|
|
if refable.Schema != nil {
|
|
refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
}
|
|
case *swspec.SchemaOrArray:
|
|
if refable.Schema != nil {
|
|
refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
}
|
|
case swspec.Schema:
|
|
return rewriteParentRef(spec, key, ref)
|
|
default:
|
|
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func rewriteParentRef(spec *swspec.Swagger, key string, ref swspec.Ref) error {
|
|
pth := key[1:]
|
|
parent, entry := path.Dir(pth), path.Base(pth)
|
|
if swspec.Debug {
|
|
log.Println("getting schema holder at:", parent)
|
|
}
|
|
|
|
pptr, err := jsonpointer.New(parent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pvalue, _, err := pptr.Get(spec)
|
|
if err != nil {
|
|
return fmt.Errorf("can't get parent for %s: %v", parent, err)
|
|
}
|
|
if swspec.Debug {
|
|
log.Printf("rewriting holder for %T", pvalue)
|
|
}
|
|
|
|
switch container := pvalue.(type) {
|
|
case swspec.Response:
|
|
if err := rewriteParentRef(spec, "#"+parent, ref); err != nil {
|
|
return err
|
|
}
|
|
|
|
case *swspec.Response:
|
|
container.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case *swspec.Responses:
|
|
statusCode, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
resp := container.StatusCodeResponses[statusCode]
|
|
resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
container.StatusCodeResponses[statusCode] = resp
|
|
|
|
case map[string]swspec.Response:
|
|
resp := container[entry]
|
|
resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
container[entry] = resp
|
|
|
|
case swspec.Parameter:
|
|
if err := rewriteParentRef(spec, "#"+parent, ref); err != nil {
|
|
return err
|
|
}
|
|
|
|
case map[string]swspec.Parameter:
|
|
param := container[entry]
|
|
param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
container[entry] = param
|
|
|
|
case []swspec.Parameter:
|
|
idx, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
param := container[idx]
|
|
param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
container[idx] = param
|
|
|
|
case swspec.Definitions:
|
|
container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case map[string]swspec.Schema:
|
|
container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case []swspec.Schema:
|
|
idx, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case *swspec.SchemaOrArray:
|
|
idx, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
default:
|
|
return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cloneSchema(schema *swspec.Schema) (*swspec.Schema, error) {
|
|
var sch swspec.Schema
|
|
if err := swag.FromDynamicJSON(schema, &sch); err != nil {
|
|
return nil, fmt.Errorf("name inlined schema: %v", err)
|
|
}
|
|
return &sch, nil
|
|
}
|
|
|
|
func importExternalReferences(opts *FlattenOpts) error {
|
|
groupedRefs := reverseIndexForSchemaRefs(opts)
|
|
|
|
for refStr, entry := range groupedRefs {
|
|
if !entry.Ref.HasFragmentOnly {
|
|
if swspec.Debug {
|
|
log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr)
|
|
}
|
|
// resolve to actual schema
|
|
sch, err := swspec.ResolveRefWithBase(opts.Swagger(), &entry.Ref, opts.ExpandOpts(false))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sch == nil {
|
|
return fmt.Errorf("no schema found at %s for [%s]", refStr, strings.Join(entry.Keys, ", "))
|
|
}
|
|
if swspec.Debug {
|
|
log.Printf("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr)
|
|
}
|
|
|
|
// generate a unique name
|
|
newName := uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref))
|
|
if swspec.Debug {
|
|
log.Printf("new name for [%s]: %s", strings.Join(entry.Keys, ", "), newName)
|
|
}
|
|
|
|
// rewrite the external refs to local ones
|
|
for _, key := range entry.Keys {
|
|
if err := updateRef(opts.Swagger(), key, swspec.MustCreateRef("#"+path.Join("/definitions", newName))); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// add the resolved schema to the definitions
|
|
saveSchema(opts.Swagger(), newName, sch)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type refRevIdx struct {
|
|
Ref swspec.Ref
|
|
Keys []string
|
|
}
|
|
|
|
func reverseIndexForSchemaRefs(opts *FlattenOpts) map[string]refRevIdx {
|
|
collected := make(map[string]refRevIdx)
|
|
for key, schRef := range opts.Spec.references.schemas {
|
|
if entry, ok := collected[schRef.String()]; ok {
|
|
entry.Keys = append(entry.Keys, key)
|
|
collected[schRef.String()] = entry
|
|
} else {
|
|
collected[schRef.String()] = refRevIdx{
|
|
Ref: schRef,
|
|
Keys: []string{key},
|
|
}
|
|
}
|
|
}
|
|
return collected
|
|
}
|
|
|
|
func nameFromRef(ref swspec.Ref) string {
|
|
u := ref.GetURL()
|
|
if u.Fragment != "" {
|
|
return swag.ToJSONName(path.Base(u.Fragment))
|
|
}
|
|
if u.Path != "" {
|
|
bn := path.Base(u.Path)
|
|
if bn != "" && bn != "/" {
|
|
ext := path.Ext(bn)
|
|
if ext != "" {
|
|
return swag.ToJSONName(bn[:len(bn)-len(ext)])
|
|
}
|
|
return swag.ToJSONName(bn)
|
|
}
|
|
}
|
|
return swag.ToJSONName(strings.Replace(u.Host, ".", " ", -1))
|
|
}
|
|
|
|
func saveSchema(spec *swspec.Swagger, name string, schema *swspec.Schema) {
|
|
if schema == nil {
|
|
return
|
|
}
|
|
if spec.Definitions == nil {
|
|
spec.Definitions = make(map[string]swspec.Schema, 150)
|
|
}
|
|
spec.Definitions[name] = *schema
|
|
}
|
|
|
|
func updateRef(spec *swspec.Swagger, key string, ref swspec.Ref) error {
|
|
if swspec.Debug {
|
|
log.Printf("updating ref for %s with %s", key, ref.String())
|
|
}
|
|
pth := key[1:]
|
|
ptr, err := jsonpointer.New(pth)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
value, _, err := ptr.Get(spec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch refable := value.(type) {
|
|
case *swspec.Schema:
|
|
refable.Ref = ref
|
|
case *swspec.SchemaOrBool:
|
|
if refable.Schema != nil {
|
|
refable.Schema.Ref = ref
|
|
}
|
|
case *swspec.SchemaOrArray:
|
|
if refable.Schema != nil {
|
|
refable.Schema.Ref = ref
|
|
}
|
|
case swspec.Schema:
|
|
parent, entry := path.Dir(pth), path.Base(pth)
|
|
if swspec.Debug {
|
|
log.Println("getting schema holder at:", parent)
|
|
}
|
|
|
|
pptr, err := jsonpointer.New(parent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pvalue, _, err := pptr.Get(spec)
|
|
if err != nil {
|
|
return fmt.Errorf("can't get parent for %s: %v", parent, err)
|
|
}
|
|
|
|
switch container := pvalue.(type) {
|
|
case swspec.Definitions:
|
|
container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case map[string]swspec.Schema:
|
|
container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case []swspec.Schema:
|
|
idx, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
case *swspec.SchemaOrArray:
|
|
idx, err := strconv.Atoi(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("%s not a number: %v", pth, err)
|
|
}
|
|
container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}}
|
|
|
|
}
|
|
|
|
default:
|
|
return fmt.Errorf("no schema with ref found at %s for %T", key, value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func containsString(names []string, name string) bool {
|
|
for _, nm := range names {
|
|
if nm == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
type opRef struct {
|
|
Method string
|
|
Path string
|
|
Key string
|
|
ID string
|
|
Op *swspec.Operation
|
|
Ref swspec.Ref
|
|
}
|
|
|
|
type opRefs []opRef
|
|
|
|
func (o opRefs) Len() int { return len(o) }
|
|
func (o opRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
|
|
func (o opRefs) Less(i, j int) bool { return o[i].Key < o[j].Key }
|
|
|
|
func gatherOperations(specDoc *Spec, operationIDs []string) map[string]opRef {
|
|
var oprefs opRefs
|
|
|
|
for method, pathItem := range specDoc.Operations() {
|
|
for pth, operation := range pathItem {
|
|
vv := *operation
|
|
oprefs = append(oprefs, opRef{
|
|
Key: swag.ToGoName(strings.ToLower(method) + " " + pth),
|
|
Method: method,
|
|
Path: pth,
|
|
ID: vv.ID,
|
|
Op: &vv,
|
|
Ref: swspec.MustCreateRef("#" + path.Join("/paths", jsonpointer.Escape(pth), method)),
|
|
})
|
|
}
|
|
}
|
|
|
|
sort.Sort(oprefs)
|
|
|
|
operations := make(map[string]opRef)
|
|
for _, opr := range oprefs {
|
|
nm := opr.ID
|
|
if nm == "" {
|
|
nm = opr.Key
|
|
}
|
|
|
|
oo, found := operations[nm]
|
|
if found && oo.Method != opr.Method && oo.Path != opr.Path {
|
|
nm = opr.Key
|
|
}
|
|
if len(operationIDs) == 0 || containsString(operationIDs, opr.ID) || containsString(operationIDs, nm) {
|
|
opr.ID = nm
|
|
opr.Op.ID = nm
|
|
operations[nm] = opr
|
|
}
|
|
}
|
|
|
|
return operations
|
|
}
|