* Add Virtual Kubelet provider for VIC Initial virtual kubelet provider for VMware VIC. This provider currently handles creating and starting of a pod VM via the VIC portlayer and persona server. Image store handling via the VIC persona server. This provider currently requires the feature/wolfpack branch of VIC. * Added pod stop and delete. Also added node capacity. Added the ability to stop and delete pod VMs via VIC. Also retrieve node capacity information from the VCH. * Cleanup and readme file Some file clean up and added a Readme.md markdown file for the VIC provider. * Cleaned up errors, added function comments, moved operation code 1. Cleaned up error handling. Set standard for creating errors. 2. Added method prototype comments for all interface functions. 3. Moved PodCreator, PodStarter, PodStopper, and PodDeleter to a new folder. * Add mocking code and unit tests for podcache, podcreator, and podstarter Used the unit test framework used in VIC to handle assertions in the provider's unit test. Mocking code generated using OSS project mockery, which is compatible with the testify assertion framework. * Vendored packages for the VIC provider Requires feature/wolfpack branch of VIC and a few specific commit sha of projects used within VIC. * Implementation of POD Stopper and Deleter unit tests (#4) * Updated files for initial PR
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
|
|
}
|