...

Source file src/github.com/go-openapi/analysis/internal/flatten/sortref/keys.go

Documentation: github.com/go-openapi/analysis/internal/flatten/sortref

     1  package sortref
     2  
     3  import (
     4  	"net/http"
     5  	"path"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/go-openapi/jsonpointer"
    10  	"github.com/go-openapi/spec"
    11  )
    12  
    13  const (
    14  	paths       = "paths"
    15  	responses   = "responses"
    16  	parameters  = "parameters"
    17  	definitions = "definitions"
    18  )
    19  
    20  var (
    21  	ignoredKeys  map[string]struct{}
    22  	validMethods map[string]struct{}
    23  )
    24  
    25  func init() {
    26  	ignoredKeys = map[string]struct{}{
    27  		"schema":     {},
    28  		"properties": {},
    29  		"not":        {},
    30  		"anyOf":      {},
    31  		"oneOf":      {},
    32  	}
    33  
    34  	validMethods = map[string]struct{}{
    35  		"GET":     {},
    36  		"HEAD":    {},
    37  		"OPTIONS": {},
    38  		"PATCH":   {},
    39  		"POST":    {},
    40  		"PUT":     {},
    41  		"DELETE":  {},
    42  	}
    43  }
    44  
    45  // Key represent a key item constructed from /-separated segments
    46  type Key struct {
    47  	Segments int
    48  	Key      string
    49  }
    50  
    51  // Keys is a sortable collable collection of Keys
    52  type Keys []Key
    53  
    54  func (k Keys) Len() int      { return len(k) }
    55  func (k Keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
    56  func (k Keys) Less(i, j int) bool {
    57  	return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key)
    58  }
    59  
    60  // KeyParts construct a SplitKey with all its /-separated segments decomposed. It is sortable.
    61  func KeyParts(key string) SplitKey {
    62  	var res []string
    63  	for _, part := range strings.Split(key[1:], "/") {
    64  		if part != "" {
    65  			res = append(res, jsonpointer.Unescape(part))
    66  		}
    67  	}
    68  
    69  	return res
    70  }
    71  
    72  // SplitKey holds of the parts of a /-separated key, so that their location may be determined.
    73  type SplitKey []string
    74  
    75  // IsDefinition is true when the split key is in the #/definitions section of a spec
    76  func (s SplitKey) IsDefinition() bool {
    77  	return len(s) > 1 && s[0] == definitions
    78  }
    79  
    80  // DefinitionName yields the name of the definition
    81  func (s SplitKey) DefinitionName() string {
    82  	if !s.IsDefinition() {
    83  		return ""
    84  	}
    85  
    86  	return s[1]
    87  }
    88  
    89  func (s SplitKey) isKeyName(i int) bool {
    90  	if i <= 0 {
    91  		return false
    92  	}
    93  
    94  	count := 0
    95  	for idx := i - 1; idx > 0; idx-- {
    96  		if s[idx] != "properties" {
    97  			break
    98  		}
    99  		count++
   100  	}
   101  
   102  	return count%2 != 0
   103  }
   104  
   105  // PartAdder know how to construct the components of a new name
   106  type PartAdder func(string) []string
   107  
   108  // BuildName builds a name from segments
   109  func (s SplitKey) BuildName(segments []string, startIndex int, adder PartAdder) string {
   110  	for i, part := range s[startIndex:] {
   111  		if _, ignored := ignoredKeys[part]; !ignored || s.isKeyName(startIndex+i) {
   112  			segments = append(segments, adder(part)...)
   113  		}
   114  	}
   115  
   116  	return strings.Join(segments, " ")
   117  }
   118  
   119  // IsOperation is true when the split key is in the operations section
   120  func (s SplitKey) IsOperation() bool {
   121  	return len(s) > 1 && s[0] == paths
   122  }
   123  
   124  // IsSharedOperationParam is true when the split key is in the parameters section of a path
   125  func (s SplitKey) IsSharedOperationParam() bool {
   126  	return len(s) > 2 && s[0] == paths && s[2] == parameters
   127  }
   128  
   129  // IsSharedParam is true when the split key is in the #/parameters section of a spec
   130  func (s SplitKey) IsSharedParam() bool {
   131  	return len(s) > 1 && s[0] == parameters
   132  }
   133  
   134  // IsOperationParam is true when the split key is in the parameters section of an operation
   135  func (s SplitKey) IsOperationParam() bool {
   136  	return len(s) > 3 && s[0] == paths && s[3] == parameters
   137  }
   138  
   139  // IsOperationResponse is true when the split key is in the responses section of an operation
   140  func (s SplitKey) IsOperationResponse() bool {
   141  	return len(s) > 3 && s[0] == paths && s[3] == responses
   142  }
   143  
   144  // IsSharedResponse is true when the split key is in the #/responses section of a spec
   145  func (s SplitKey) IsSharedResponse() bool {
   146  	return len(s) > 1 && s[0] == responses
   147  }
   148  
   149  // IsDefaultResponse is true when the split key is the default response for an operation
   150  func (s SplitKey) IsDefaultResponse() bool {
   151  	return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default"
   152  }
   153  
   154  // IsStatusCodeResponse is true when the split key is an operation response with a status code
   155  func (s SplitKey) IsStatusCodeResponse() bool {
   156  	isInt := func() bool {
   157  		_, err := strconv.Atoi(s[4])
   158  
   159  		return err == nil
   160  	}
   161  
   162  	return len(s) > 4 && s[0] == paths && s[3] == responses && isInt()
   163  }
   164  
   165  // ResponseName yields either the status code or "Default" for a response
   166  func (s SplitKey) ResponseName() string {
   167  	if s.IsStatusCodeResponse() {
   168  		code, _ := strconv.Atoi(s[4])
   169  
   170  		return http.StatusText(code)
   171  	}
   172  
   173  	if s.IsDefaultResponse() {
   174  		return "Default"
   175  	}
   176  
   177  	return ""
   178  }
   179  
   180  // PathItemRef constructs a $ref object from a split key of the form /{path}/{method}
   181  func (s SplitKey) PathItemRef() spec.Ref {
   182  	if len(s) < 3 {
   183  		return spec.Ref{}
   184  	}
   185  
   186  	pth, method := s[1], s[2]
   187  	if _, isValidMethod := validMethods[strings.ToUpper(method)]; !isValidMethod && !strings.HasPrefix(method, "x-") {
   188  		return spec.Ref{}
   189  	}
   190  
   191  	return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(pth), strings.ToUpper(method)))
   192  }
   193  
   194  // PathRef constructs a $ref object from a split key of the form /paths/{reference}
   195  func (s SplitKey) PathRef() spec.Ref {
   196  	if !s.IsOperation() {
   197  		return spec.Ref{}
   198  	}
   199  
   200  	return spec.MustCreateRef("#" + path.Join("/", paths, jsonpointer.Escape(s[1])))
   201  }
   202  

View as plain text