1
16
17 package schemaconv
18
19 import (
20 "errors"
21 "path"
22 "strings"
23
24 "k8s.io/kube-openapi/pkg/validation/spec"
25 "sigs.k8s.io/structured-merge-diff/v4/schema"
26 )
27
28
29
30
31
32
33
34
35
36
37
38 func ToSchemaFromOpenAPI(models map[string]*spec.Schema, preserveUnknownFields bool) (*schema.Schema, error) {
39 c := convert{
40 preserveUnknownFields: preserveUnknownFields,
41 output: &schema.Schema{},
42 }
43
44 for name, spec := range models {
45
46 if len(spec.Ref.String()) > 0 {
47 continue
48 }
49
50 var a schema.Atom
51
52
53
54 if name == quantityResource {
55 a = schema.Atom{
56 Scalar: untypedDef.Atom.Scalar,
57 }
58 } else if name == rawExtensionResource {
59 a = untypedDef.Atom
60 } else {
61 c2 := c.push(name, &a)
62 c2.visitSpec(spec)
63 c.pop(c2)
64 }
65
66 c.insertTypeDef(name, a)
67 }
68
69 if len(c.errorMessages) > 0 {
70 return nil, errors.New(strings.Join(c.errorMessages, "\n"))
71 }
72
73 c.addCommonTypes()
74 return c.output, nil
75 }
76
77 func (c *convert) visitSpec(m *spec.Schema) {
78
79 if p, ok := m.Extensions["x-kubernetes-preserve-unknown-fields"]; ok && p == true {
80 c.preserveUnknownFields = true
81 }
82 a := c.top()
83 *a = c.parseSchema(m)
84 }
85
86 func (c *convert) parseSchema(m *spec.Schema) schema.Atom {
87
88
89
90 typ := ""
91 if len(m.Type) > 0 {
92 typ = m.Type[0]
93 }
94
95
96
97 switch typ {
98 case "":
99
100
101
102
103 return schema.Atom{
104 Scalar: ptr(schema.Scalar("untyped")),
105 List: c.parseList(m),
106 Map: c.parseObject(m),
107 }
108
109 case "object":
110 return schema.Atom{
111 Map: c.parseObject(m),
112 }
113 case "array":
114 return schema.Atom{
115 List: c.parseList(m),
116 }
117 case "integer", "boolean", "number", "string":
118 return convertPrimitive(typ, m.Format)
119 default:
120 c.reportError("unrecognized type: '%v'", typ)
121 return schema.Atom{
122 Scalar: ptr(schema.Scalar("untyped")),
123 }
124 }
125 }
126
127 func (c *convert) makeOpenAPIRef(specSchema *spec.Schema) schema.TypeRef {
128 refString := specSchema.Ref.String()
129
130
131 if len(refString) == 0 && len(specSchema.AllOf) == 1 && len(specSchema.AllOf[0].Ref.String()) > 0 {
132 refString = specSchema.AllOf[0].Ref.String()
133 }
134
135 if _, n := path.Split(refString); len(n) > 0 {
136
137
138
139
140
141 mapRelationship, err := getMapElementRelationship(specSchema.Extensions)
142 if err != nil {
143 c.reportError(err.Error())
144 }
145
146 if len(mapRelationship) > 0 {
147 return schema.TypeRef{
148 NamedType: &n,
149 ElementRelationship: &mapRelationship,
150 }
151 }
152
153 return schema.TypeRef{
154 NamedType: &n,
155 }
156
157 }
158 var inlined schema.Atom
159
160
161 c2 := c.push("inlined in "+c.currentName, &inlined)
162 c2.preserveUnknownFields = c.preserveUnknownFields
163 c2.visitSpec(specSchema)
164 c.pop(c2)
165
166 return schema.TypeRef{
167 Inlined: inlined,
168 }
169 }
170
171 func (c *convert) parseObject(s *spec.Schema) *schema.Map {
172 var fields []schema.StructField
173 for name, member := range s.Properties {
174 fields = append(fields, schema.StructField{
175 Name: name,
176 Type: c.makeOpenAPIRef(&member),
177 Default: member.Default,
178 })
179 }
180
181
182
183 elementType := func() schema.TypeRef {
184 if s.AdditionalProperties == nil {
185
186
187 if c.preserveUnknownFields || len(s.Properties) == 0 {
188 return schema.TypeRef{
189 NamedType: &deducedName,
190 }
191 }
192
193
194
195 return schema.TypeRef{}
196 } else if s.AdditionalProperties.Schema != nil {
197
198 return c.makeOpenAPIRef(s.AdditionalProperties.Schema)
199
200 } else if s.AdditionalProperties.Allows {
201
202
203 return schema.TypeRef{
204 NamedType: &deducedName,
205 }
206 } else {
207
208
209 return schema.TypeRef{}
210 }
211 }()
212
213 relationship, err := getMapElementRelationship(s.Extensions)
214 if err != nil {
215 c.reportError(err.Error())
216 }
217
218 return &schema.Map{
219 Fields: fields,
220 ElementRelationship: relationship,
221 ElementType: elementType,
222 }
223 }
224
225 func (c *convert) parseList(s *spec.Schema) *schema.List {
226 relationship, mapKeys, err := getListElementRelationship(s.Extensions)
227 if err != nil {
228 c.reportError(err.Error())
229 }
230 elementType := func() schema.TypeRef {
231 if s.Items != nil {
232 if s.Items.Schema == nil || s.Items.Len() != 1 {
233 c.reportError("structural schema arrays must have exactly one member subtype")
234 return schema.TypeRef{
235 NamedType: &deducedName,
236 }
237 }
238
239 subSchema := s.Items.Schema
240 if subSchema == nil {
241 subSchema = &s.Items.Schemas[0]
242 }
243 return c.makeOpenAPIRef(subSchema)
244 } else if len(s.Type) > 0 && len(s.Type[0]) > 0 {
245 c.reportError("`items` must be specified on arrays")
246 }
247
248
249 return schema.TypeRef{
250 NamedType: &untypedName,
251 }
252
253 }()
254
255 return &schema.List{
256 ElementRelationship: relationship,
257 Keys: mapKeys,
258 ElementType: elementType,
259 }
260 }
261
View as plain text