1
16
17 package proto
18
19 import (
20 "fmt"
21 "sort"
22 "strings"
23
24 openapi_v2 "github.com/google/gnostic-models/openapiv2"
25 "gopkg.in/yaml.v2"
26 )
27
28 func newSchemaError(path *Path, format string, a ...interface{}) error {
29 err := fmt.Sprintf(format, a...)
30 if path.Len() == 0 {
31 return fmt.Errorf("SchemaError: %v", err)
32 }
33 return fmt.Errorf("SchemaError(%v): %v", path, err)
34 }
35
36
37 func VendorExtensionToMap(e []*openapi_v2.NamedAny) map[string]interface{} {
38 values := map[string]interface{}{}
39
40 for _, na := range e {
41 if na.GetName() == "" || na.GetValue() == nil {
42 continue
43 }
44 if na.GetValue().GetYaml() == "" {
45 continue
46 }
47 var value interface{}
48 err := yaml.Unmarshal([]byte(na.GetValue().GetYaml()), &value)
49 if err != nil {
50 continue
51 }
52
53 values[na.GetName()] = value
54 }
55
56 return values
57 }
58
59
60
61 type Definitions struct {
62 models map[string]Schema
63 }
64
65 var _ Models = &Definitions{}
66
67
68 func NewOpenAPIData(doc *openapi_v2.Document) (Models, error) {
69 definitions := Definitions{
70 models: map[string]Schema{},
71 }
72
73
74
75 for _, namedSchema := range doc.GetDefinitions().GetAdditionalProperties() {
76 definitions.models[namedSchema.GetName()] = nil
77 }
78
79
80 for _, namedSchema := range doc.GetDefinitions().GetAdditionalProperties() {
81 path := NewPath(namedSchema.GetName())
82 schema, err := definitions.ParseSchema(namedSchema.GetValue(), &path)
83 if err != nil {
84 return nil, err
85 }
86 definitions.models[namedSchema.GetName()] = schema
87 }
88
89 return &definitions, nil
90 }
91
92
93
94 func (d *Definitions) parseReference(s *openapi_v2.Schema, path *Path) (Schema, error) {
95
96 if len(s.GetProperties().GetAdditionalProperties()) > 0 {
97 return nil, newSchemaError(path, "unallowed embedded type definition")
98 }
99
100 if len(s.GetType().GetValue()) > 0 {
101 return nil, newSchemaError(path, "definition reference can't have a type")
102 }
103
104
105 if !strings.HasPrefix(s.GetXRef(), "#/definitions/") {
106 return nil, newSchemaError(path, "unallowed reference to non-definition %q", s.GetXRef())
107 }
108 reference := strings.TrimPrefix(s.GetXRef(), "#/definitions/")
109 if _, ok := d.models[reference]; !ok {
110 return nil, newSchemaError(path, "unknown model in reference: %q", reference)
111 }
112 base, err := d.parseBaseSchema(s, path)
113 if err != nil {
114 return nil, err
115 }
116 return &Ref{
117 BaseSchema: base,
118 reference: reference,
119 definitions: d,
120 }, nil
121 }
122
123 func parseDefault(def *openapi_v2.Any) (interface{}, error) {
124 if def == nil {
125 return nil, nil
126 }
127 var i interface{}
128 if err := yaml.Unmarshal([]byte(def.Yaml), &i); err != nil {
129 return nil, err
130 }
131 return i, nil
132 }
133
134 func (d *Definitions) parseBaseSchema(s *openapi_v2.Schema, path *Path) (BaseSchema, error) {
135 def, err := parseDefault(s.GetDefault())
136 if err != nil {
137 return BaseSchema{}, err
138 }
139 return BaseSchema{
140 Description: s.GetDescription(),
141 Default: def,
142 Extensions: VendorExtensionToMap(s.GetVendorExtension()),
143 Path: *path,
144 }, nil
145 }
146
147
148 func (d *Definitions) parseMap(s *openapi_v2.Schema, path *Path) (Schema, error) {
149 if len(s.GetType().GetValue()) != 0 && s.GetType().GetValue()[0] != object {
150 return nil, newSchemaError(path, "invalid object type")
151 }
152 var sub Schema
153
154 if s.GetAdditionalProperties().GetSchema() == nil {
155 base, err := d.parseBaseSchema(s, path)
156 if err != nil {
157 return nil, err
158 }
159 sub = &Arbitrary{
160 BaseSchema: base,
161 }
162 } else {
163 var err error
164 sub, err = d.ParseSchema(s.GetAdditionalProperties().GetSchema(), path)
165 if err != nil {
166 return nil, err
167 }
168 }
169 base, err := d.parseBaseSchema(s, path)
170 if err != nil {
171 return nil, err
172 }
173 return &Map{
174 BaseSchema: base,
175 SubType: sub,
176 }, nil
177 }
178
179 func (d *Definitions) parsePrimitive(s *openapi_v2.Schema, path *Path) (Schema, error) {
180 var t string
181 if len(s.GetType().GetValue()) > 1 {
182 return nil, newSchemaError(path, "primitive can't have more than 1 type")
183 }
184 if len(s.GetType().GetValue()) == 1 {
185 t = s.GetType().GetValue()[0]
186 }
187 switch t {
188 case String:
189 case Number:
190 case Integer:
191 case Boolean:
192
193 default:
194 return nil, newSchemaError(path, "Unknown primitive type: %q", t)
195 }
196 base, err := d.parseBaseSchema(s, path)
197 if err != nil {
198 return nil, err
199 }
200 return &Primitive{
201 BaseSchema: base,
202 Type: t,
203 Format: s.GetFormat(),
204 }, nil
205 }
206
207 func (d *Definitions) parseArray(s *openapi_v2.Schema, path *Path) (Schema, error) {
208 if len(s.GetType().GetValue()) != 1 {
209 return nil, newSchemaError(path, "array should have exactly one type")
210 }
211 if s.GetType().GetValue()[0] != array {
212 return nil, newSchemaError(path, `array should have type "array"`)
213 }
214 if len(s.GetItems().GetSchema()) != 1 {
215
216
217 return nil, newSchemaError(path, "array should have exactly one sub-item")
218 }
219 sub, err := d.ParseSchema(s.GetItems().GetSchema()[0], path)
220 if err != nil {
221 return nil, err
222 }
223 base, err := d.parseBaseSchema(s, path)
224 if err != nil {
225 return nil, err
226 }
227 return &Array{
228 BaseSchema: base,
229 SubType: sub,
230 }, nil
231 }
232
233 func (d *Definitions) parseKind(s *openapi_v2.Schema, path *Path) (Schema, error) {
234 if len(s.GetType().GetValue()) != 0 && s.GetType().GetValue()[0] != object {
235 return nil, newSchemaError(path, "invalid object type")
236 }
237 if s.GetProperties() == nil {
238 return nil, newSchemaError(path, "object doesn't have properties")
239 }
240
241 fields := map[string]Schema{}
242 fieldOrder := []string{}
243
244 for _, namedSchema := range s.GetProperties().GetAdditionalProperties() {
245 var err error
246 name := namedSchema.GetName()
247 path := path.FieldPath(name)
248 fields[name], err = d.ParseSchema(namedSchema.GetValue(), &path)
249 if err != nil {
250 return nil, err
251 }
252 fieldOrder = append(fieldOrder, name)
253 }
254
255 base, err := d.parseBaseSchema(s, path)
256 if err != nil {
257 return nil, err
258 }
259 return &Kind{
260 BaseSchema: base,
261 RequiredFields: s.GetRequired(),
262 Fields: fields,
263 FieldOrder: fieldOrder,
264 }, nil
265 }
266
267 func (d *Definitions) parseArbitrary(s *openapi_v2.Schema, path *Path) (Schema, error) {
268 base, err := d.parseBaseSchema(s, path)
269 if err != nil {
270 return nil, err
271 }
272 return &Arbitrary{
273 BaseSchema: base,
274 }, nil
275 }
276
277
278
279 func (d *Definitions) ParseSchema(s *openapi_v2.Schema, path *Path) (Schema, error) {
280 if s.GetXRef() != "" {
281
282
283 return d.parseReference(s, path)
284 }
285 objectTypes := s.GetType().GetValue()
286 switch len(objectTypes) {
287 case 0:
288
289
290
291 if s.GetProperties() != nil {
292
293
294 return d.parseKind(s, path)
295 } else {
296
297
298
299 return d.parseArbitrary(s, path)
300 }
301 case 1:
302 t := objectTypes[0]
303 switch t {
304 case object:
305 if s.GetProperties() != nil {
306 return d.parseKind(s, path)
307 } else {
308 return d.parseMap(s, path)
309 }
310 case array:
311 return d.parseArray(s, path)
312 }
313 return d.parsePrimitive(s, path)
314 default:
315
316
317
318 return nil, newSchemaError(path, "definitions with multiple types aren't supported")
319 }
320 }
321
322
323
324 func (d *Definitions) LookupModel(model string) Schema {
325 return d.models[model]
326 }
327
328 func (d *Definitions) ListModels() []string {
329 models := []string{}
330
331 for model := range d.models {
332 models = append(models, model)
333 }
334
335 sort.Strings(models)
336 return models
337 }
338
339 type Ref struct {
340 BaseSchema
341
342 reference string
343 definitions *Definitions
344 }
345
346 var _ Reference = &Ref{}
347
348 func (r *Ref) Reference() string {
349 return r.reference
350 }
351
352 func (r *Ref) SubSchema() Schema {
353 return r.definitions.models[r.reference]
354 }
355
356 func (r *Ref) Accept(v SchemaVisitor) {
357 v.VisitReference(r)
358 }
359
360 func (r *Ref) GetName() string {
361 return fmt.Sprintf("Reference to %q", r.reference)
362 }
363
View as plain text