-
Notifications
You must be signed in to change notification settings - Fork 73
/
visitor.go
122 lines (114 loc) · 4.11 KB
/
visitor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package definitions
import (
"k8s.io/kube-openapi/pkg/util/proto"
)
// schemaFieldVisitor implements proto.SchemaVisitor and turns a given schema into a definitionField.
type schemaFieldVisitor struct {
field definitionField
definitions map[string]definition
models proto.Models
}
// VisitArray turns an array into a definitionField (stored on the receiver). For arrays of complex types, will also
// visit the subtype.
func (s *schemaFieldVisitor) VisitArray(array *proto.Array) {
field := definitionField{
Description: array.GetDescription(),
}
// this currently is not recursive and provides little information for nested types- while this isn't optimal,
// it was kept this way to provide backwards compat with previous endpoints.
array.SubType.Accept(s)
subField := s.field
field.Type = "array"
field.SubType = subField.Type
s.field = field
}
// VisitMap turns a map into a definitionField (stored on the receiver). For maps of complex types, will also visit the
// subtype.
func (s *schemaFieldVisitor) VisitMap(protoMap *proto.Map) {
field := definitionField{
Description: protoMap.GetDescription(),
}
// this currently is not recursive and provides little information for nested types- while this isn't optimal,
// it was kept this way to provide backwards compat with previous endpoints.
protoMap.SubType.Accept(s)
subField := s.field
field.Type = "map"
field.SubType = subField.Type
s.field = field
}
// VisitPrimitive turns a primitive into a definitionField (stored on the receiver).
func (s *schemaFieldVisitor) VisitPrimitive(primitive *proto.Primitive) {
field := definitionField{
Description: primitive.GetDescription(),
}
if primitive.Type == "number" || primitive.Type == "integer" {
field.Type = "int"
} else {
field.Type = primitive.Type
}
s.field = field
}
// VisitKind turns a kind into a definitionField and a definition. Both are stored on the receiver.
func (s *schemaFieldVisitor) VisitKind(kind *proto.Kind) {
path := kind.Path.String()
field := definitionField{
Description: kind.GetDescription(),
Type: path,
}
if _, ok := s.definitions[path]; ok {
// if we have already seen this kind, we don't want to re-evaluate the definition. Some kinds can be
// recursive through use of references, so this circuit-break is necessary to avoid infinite loops
s.field = field
return
}
schemaDefinition := definition{
ResourceFields: map[string]definitionField{},
Type: path,
Description: kind.GetDescription(),
}
// this definition may refer to itself, so we mark this as seen to not infinitely recurse
s.definitions[path] = definition{}
for fieldName, schemaField := range kind.Fields {
schemaField.Accept(s)
schemaDefinition.ResourceFields[fieldName] = s.field
}
for _, field := range kind.RequiredFields {
current, ok := schemaDefinition.ResourceFields[field]
if !ok {
// this does silently ignore inconsistent kinds that list
continue
}
current.Required = true
schemaDefinition.ResourceFields[field] = current
}
s.definitions[path] = schemaDefinition
// the visitor may have set the field multiple times while evaluating kind fields, so we only set the final
// kind-based field at the end
s.field = field
}
// VisitReference turns a reference into a definitionField. Will also visit the referred type.
func (s *schemaFieldVisitor) VisitReference(ref proto.Reference) {
sub := ref.SubSchema()
if sub == nil {
// if we don't have a sub-schema defined, we can't extract much meaningful information
field := definitionField{
Description: ref.GetDescription(),
Type: ref.Reference(),
}
s.field = field
return
}
sub.Accept(s)
field := s.field
field.Description = ref.GetDescription()
s.field = field
}
// VisitArbitrary turns an abitrary (item with no type) into a definitionField (stored on the receiver).
func (s *schemaFieldVisitor) VisitArbitrary(arb *proto.Arbitrary) {
// In certain cases k8s seems to not provide a type for certain fields. We assume for the
// purposes of this visitor that all of these have a type of string.
s.field = definitionField{
Description: arb.GetDescription(),
Type: "string",
}
}