...
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
46 type Key struct {
47 Segments int
48 Key string
49 }
50
51
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
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
73 type SplitKey []string
74
75
76 func (s SplitKey) IsDefinition() bool {
77 return len(s) > 1 && s[0] == definitions
78 }
79
80
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
106 type PartAdder func(string) []string
107
108
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
120 func (s SplitKey) IsOperation() bool {
121 return len(s) > 1 && s[0] == paths
122 }
123
124
125 func (s SplitKey) IsSharedOperationParam() bool {
126 return len(s) > 2 && s[0] == paths && s[2] == parameters
127 }
128
129
130 func (s SplitKey) IsSharedParam() bool {
131 return len(s) > 1 && s[0] == parameters
132 }
133
134
135 func (s SplitKey) IsOperationParam() bool {
136 return len(s) > 3 && s[0] == paths && s[3] == parameters
137 }
138
139
140 func (s SplitKey) IsOperationResponse() bool {
141 return len(s) > 3 && s[0] == paths && s[3] == responses
142 }
143
144
145 func (s SplitKey) IsSharedResponse() bool {
146 return len(s) > 1 && s[0] == responses
147 }
148
149
150 func (s SplitKey) IsDefaultResponse() bool {
151 return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default"
152 }
153
154
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
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
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
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