Skip to content

Commit bf5f540

Browse files
AlessioDiamaAlessio.Diamanti
authored andcommitted
extractDependenciesFromMap new implementation
1 parent 8a15e8e commit bf5f540

File tree

4 files changed

+121
-42
lines changed

4 files changed

+121
-42
lines changed

examples/kubernetes/recursive-custom-types/instance.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,65 @@ spec:
88
roles:
99
- name: CEO
1010
seniority: Senior
11+
aliases:
12+
- SupremeChief
1113
- name: CFO
1214
seniority: Senior
15+
aliases:
16+
- MoneyBoss
1317
- name: Engineer
1418
seniority: Senior
19+
aliases:
20+
- Developer
1521
- name: Intern
1622
seniority: Junior
23+
aliases:
24+
- Trainee
1725
- name: Marketer
1826
seniority: Mid
27+
aliases:
28+
- Marketing Specialist
1929
- name: Social Media Manager
2030
seniority: Mid
31+
aliases:
32+
- Social Media Specialist
2133
management:
2234
- name: Mario
2335
role:
2436
name: CEO
2537
seniority: Senior
38+
colleagues:
39+
luigi:
40+
name: Secretary
41+
seniority: Mid
2642
- name: Luigi
2743
role:
2844
name: CFO
2945
seniority: Senior
46+
colleagues:
47+
viktor:
48+
name: Assistant
49+
seniority: Mid
3050
divisions:
3151
- name: Engineering
3252
employees:
3353
- name: Peach
3454
role:
3555
name: Engineer
3656
seniority: Senior
57+
colleagues:
58+
daisy:
59+
name: Engineer
60+
seniority: Mid
61+
aliases: [DevOps]
3762
- name: Toad
3863
role:
3964
name: Intern
4065
seniority: Junior
66+
colleagues:
67+
tony:
68+
name: Intern
69+
seniority: Junior
4170
- name: Marketing
4271
employees:
4372
- name: Yoshi

examples/kubernetes/recursive-custom-types/rg.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,19 @@ spec:
88
Person:
99
name: string
1010
role: "Role"
11+
colleagues: "map[string]Role"
12+
address:
13+
street: string
14+
city: string
15+
state: string
16+
zip: string
1117
Division:
1218
name: string
1319
employees: "[]Person"
1420
Role:
1521
name: string
1622
seniority: string
23+
aliases: "[]string"
1724
spec:
1825
name: string
1926
namespace: string | default=default
@@ -37,5 +44,5 @@ spec:
3744
${schema.spec.management.map(e, e.name + " (" + e.role.name + ")").join(", ")}
3845
divisions: |
3946
${schema.spec.divisions.map(d, d.name + ": " + d.employees.map(e, e.name).join(", ")).join("\n")}
40-
roles: |
41-
${schema.spec.roles.map(r, r.name + " (" + r.seniority + ")").join(", ")}
47+
# roles: |
48+
# ${schema.spec.roles.map(r, r.name + " (" + r.seniority.join(", ") + ")").join(", ")}

pkg/simpleschema/atomic.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ func isSliceType(s string) bool {
7979
return strings.HasPrefix(s, "[]")
8080
}
8181

82+
func isObjectType(s string) bool {
83+
return s == "object"
84+
}
85+
8286
// parseMapType parses a map type string and returns the key and value types.
8387
func parseMapType(s string) (string, string, error) {
8488
if !strings.HasPrefix(s, "map[") {

pkg/simpleschema/transform.go

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,13 @@ func (t *transformer) loadPreDefinedTypes(obj map[string]interface{}) error {
8181

8282
// Build dependencies and construct the schema
8383
for k, v := range obj {
84-
dependencies := extractDependenciesFromMap(v)
84+
dependencies, err := extractDependenciesFromMap(v)
85+
if err != nil {
86+
return fmt.Errorf("failed to extract dependencies for type %s: %w", k, err)
87+
}
8588

8689
// Add dependencies to the DAG and check for cycles
87-
err := dagInstance.AddDependencies(k, dependencies)
90+
err = dagInstance.AddDependencies(k, dependencies)
8891
if err != nil {
8992
var cycleErr *dag.CycleError[string]
9093
if errors.As(err, &cycleErr) {
@@ -123,51 +126,87 @@ func (t *transformer) loadPreDefinedTypes(obj map[string]interface{}) error {
123126
return nil
124127
}
125128

126-
// Define the set of basic types as
127-
// defined in https://kro.run/docs/concepts/simple-schema#basic-types
128-
var excludedTypes = map[string]struct{}{
129-
"string": {},
130-
"integer": {},
131-
"bool": {},
132-
"float": {},
133-
"object": {},
129+
func extractDependenciesFromMap(obj interface{}) (dependencies []string, err error) {
130+
dependenciesSet := sets.Set[string]{}
131+
132+
// Extract dependencies using a helper function
133+
if rootMap, ok := obj.(map[string]interface{}); ok {
134+
err := parseMap(rootMap, dependenciesSet)
135+
if err != nil {
136+
return nil, err
137+
}
138+
}
139+
return dependenciesSet.UnsortedList(), nil
134140
}
135141

136-
func extractDependenciesFromMap(obj interface{}) []string {
137-
dependencies := sets.Set[string]{}
138-
139-
// Use a recursive helper function to traverse the map and extract dependencies
140-
var parseMap func(map[string]interface{})
141-
parseMap = func(m map[string]interface{}) {
142-
for _, value := range m {
143-
switch v := value.(type) {
144-
case string:
145-
// Strip array prefixes like "[]"
146-
trimmed := strings.TrimPrefix(v, "[]")
147-
148-
// Filter out primitive types
149-
if _, isExcluded := excludedTypes[trimmed]; !isExcluded {
150-
dependencies.Insert(trimmed)
151-
}
152-
case map[string]interface{}:
153-
// Recursively parse nested maps
154-
parseMap(v)
155-
case []interface{}:
156-
// Handle slices of types (e.g., []string or [][nested type])
157-
for _, elem := range v {
158-
if nestedMap, ok := elem.(map[string]interface{}); ok {
159-
parseMap(nestedMap)
160-
}
161-
}
142+
func handleStringType(v string, dependencies sets.Set[string]) error {
143+
// Check if the value is an atomic type
144+
if isAtomicType(v) {
145+
return nil
146+
}
147+
// Check if the value is a collection type
148+
if isCollectionType(v) {
149+
if isSliceType(v) {
150+
// It is a slice, we add the type as dependency
151+
elementType, err := parseSliceType(v)
152+
if err != nil {
153+
return fmt.Errorf("Failed to parse slice type %s: %w", v, err)
154+
}
155+
if !isAtomicType(elementType) {
156+
dependencies.Insert(elementType)
162157
}
158+
return nil
159+
} else if isMapType(v) {
160+
keyType, valueType, err := parseMapType(v)
161+
if err != nil {
162+
return fmt.Errorf("Failed to parse map type %s: %w", v, err)
163+
}
164+
// Only strings are supported as map keys
165+
if keyType != keyTypeString {
166+
return fmt.Errorf("unsupported key type for maps, only strings are supported key types: %s", keyType)
167+
}
168+
// If the value is not an atomic type, add to dependencies
169+
if !isAtomicType(valueType) {
170+
dependencies.Insert(strings.TrimPrefix(valueType, "[]"))
171+
}
172+
return nil
163173
}
164174
}
165175

166-
if rootMap, ok := obj.(map[string]interface{}); ok {
167-
parseMap(rootMap)
176+
// If the type is object, we do not add any dependency
177+
// As unstructured objects are not validated https://kro.run/docs/concepts/simple-schema#unstructured-objects
178+
if isObjectType(v) {
179+
return nil
168180
}
181+
// At this point, we have a new custom type, we add it as dependency
182+
dependencies.Insert(v)
183+
return nil
184+
}
169185

170-
return dependencies.UnsortedList()
186+
func parseMap(m map[string]interface{}, dependencies sets.Set[string]) (err error) {
187+
188+
for _, value := range m {
189+
switch v := value.(type) {
190+
case map[string]interface{}:
191+
// Recursively parse nested maps
192+
if err := parseMap(v, dependencies); err != nil {
193+
return err
194+
}
195+
case []interface{}:
196+
// Handle slices of types (e.g., []string or [][nested type])
197+
for key, elem := range v {
198+
print(key)
199+
if nestedMap, ok := elem.(map[string]interface{}); ok {
200+
parseMap(nestedMap, dependencies)
201+
} else {
202+
return fmt.Errorf("unexpected type in slice: %T", elem)
203+
}
204+
}
205+
case string:
206+
handleStringType(v, dependencies)
207+
}
208+
}
209+
return nil
171210
}
172211

173212
// buildOpenAPISchema builds an OpenAPI schema from the given object
@@ -262,7 +301,7 @@ func (tf *transformer) handleMapType(key, fieldType string) (*extv1.JSONSchemaPr
262301
return nil, fmt.Errorf("failed to parse map type for %s: %w", key, err)
263302
}
264303
if keyType != keyTypeString {
265-
return nil, fmt.Errorf("unsupported key type for maps: %s", keyType)
304+
return nil, fmt.Errorf("unsupported key type for maps, only strings are supported key types: %s", keyType)
266305
}
267306

268307
fieldJSONSchemaProps := &extv1.JSONSchemaProps{

0 commit comments

Comments
 (0)