Skip to content

Commit

Permalink
fix: added tests and handle required recursively
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsinai committed May 17, 2024
1 parent ef52087 commit 1b79c7c
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 85 deletions.
28 changes: 2 additions & 26 deletions pkg/crd/crd.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,8 @@ func convertToPortSchemas(crd v1.CustomResourceDefinition) ([]port.Action, *port
}

// Make nested schemas shallow with `NestedSchemaSeparator`(__) separator
required := []string{}
shallowedProperties := map[string]v1.JSONSchemaProps{}
handleNestedSchema(&spec, "", &required, &shallowedProperties)

bytesNested, err := json.Marshal(&spec)
shallowedSchema := ShallowJsonSchema(&spec, NestedSchemaSeparator)
bytesNested, err := json.Marshal(&shallowedSchema)
if err != nil {
return nil, nil, fmt.Errorf("error marshaling schema: %v", err)
}
Expand Down Expand Up @@ -322,27 +319,6 @@ func findMatchingCRDs(crds []v1.CustomResourceDefinition, pattern string) []v1.C
return matchedCRDs
}

func handleNestedSchema(schema *v1.JSONSchemaProps, parent string, required *[]string, shallowedProperties *map[string]v1.JSONSchemaProps) {
for k, v := range schema.Properties {
shallowedKey := k

if parent != "" {
shallowedKey = parent + NestedSchemaSeparator + k
}

if v.Type == "object" {
for _, r := range v.Required {
if v.Properties[r].Type != "object" {
*required = append(*required, shallowedKey+NestedSchemaSeparator+r)
}
}
handleNestedSchema(&v, shallowedKey, required, shallowedProperties)
} else {
(*shallowedProperties)[shallowedKey] = v
}
}
}

func handleCRD(crds []v1.CustomResourceDefinition, portConfig *port.IntegrationAppConfig, portClient *cli.PortClient) {
matchedCRDs := findMatchingCRDs(crds, portConfig.CRDSToDiscover)

Expand Down
59 changes: 0 additions & 59 deletions pkg/crd/crd_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package crd

import (
"fmt"
"slices"
"testing"

Expand Down Expand Up @@ -297,61 +296,3 @@ func TestCRD_crd_autoDiscoverCRDsToActionsNoCRDs(t *testing.T) {

testUtils.CheckResourcesExistence(false, f.portClient, t, []string{"testkind"}, []string{}, []string{"create_testkind", "update_testkind", "delete_testkind"})
}

func TestCRD_crd_andleNestedSchema(t *testing.T) {
originalSchema := &v1.JSONSchemaProps{
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"spec": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"stringProperty": {
Type: "string",
},
"intProperty": {
Type: "integer",
},
"boolProperty": {
Type: "boolean",
},
"nestedProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedStringProperty": {
Type: "string",
},
"nestedIntProperty": {
Type: "integer",
},
},
Required: []string{"nestedStringProperty"},
},
"multiNestedProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedObjectProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedStringProperty": {
Type: "string",
},
},
Required: []string{"nestedStringProperty"},
},
},
Required: []string{},
},
},
Required: []string{"stringProperty", "nestedProperty", "multiNestedProperty"},
},
},
Required: []string{"spec"},
}

required := []string{}
properties := map[string]v1.JSONSchemaProps{}

handleNestedSchema(originalSchema, "", &required, &properties)

fmt.Println(originalSchema)
}
62 changes: 62 additions & 0 deletions pkg/crd/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package crd

import (
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

func ShallowJsonSchema(schema *v1.JSONSchemaProps, separator string) *v1.JSONSchemaProps {
clonedSchema := schema.DeepCopy()
shallowProperties(clonedSchema, "", separator, clonedSchema)

if schema.Type == "object" {
return &v1.JSONSchemaProps{
Type: "object",
Properties: clonedSchema.Properties,
Required: shallowRequired(schema, "", separator),
}
}

return schema
}

func shallowProperties(schema *v1.JSONSchemaProps, parent string, seperator string, originalSchema *v1.JSONSchemaProps) {
for k, v := range schema.Properties {
shallowedKey := k

if parent != "" {
shallowedKey = parent + seperator + k
}

if v.Type != "object" {
originalSchema.Properties[shallowedKey] = v
} else {
shallowProperties(&v, shallowedKey, seperator, originalSchema)
delete(originalSchema.Properties, k)
}
}
}

// shallowRequired recursively traverses the JSONSchemaProps and returns a list of required fields with nested field names concatenated by the provided separator.
func shallowRequired(schema *v1.JSONSchemaProps, prefix, separator string) []string {
var requiredFields []string

for _, field := range schema.Required {
if propSchema, ok := schema.Properties[field]; ok {
fullFieldName := field
if prefix != "" {
fullFieldName = prefix + separator + field
}

if propSchema.Type == "object" {
// Recursively process nested objects but don't add the object field itself
nestedRequiredFields := shallowRequired(&propSchema, fullFieldName, separator)
requiredFields = append(requiredFields, nestedRequiredFields...)
} else {
// Add non-object fields to the required list
requiredFields = append(requiredFields, fullFieldName)
}
}
}

return requiredFields
}
92 changes: 92 additions & 0 deletions pkg/crd/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package crd

import (
"reflect"
"testing"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

func TestCRD_crd_shallowNestedSchema(t *testing.T) {
originalSchema := &v1.JSONSchemaProps{
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"spec": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"stringProperty": {
Type: "string",
},
"intProperty": {
Type: "integer",
},
"boolProperty": {
Type: "boolean",
},
"nestedProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedStringProperty": {
Type: "string",
},
"nestedIntProperty": {
Type: "integer",
},
},
Required: []string{"nestedStringProperty"},
},
"multiNestedProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedObjectProperty": {
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"nestedStringProperty": {
Type: "string",
},
},
Required: []string{"nestedStringProperty"},
},
},
Required: []string{},
},
},
Required: []string{"stringProperty", "nestedProperty", "multiNestedProperty"},
},
},
Required: []string{"spec"},
}

shallowedSchema := ShallowJsonSchema(originalSchema, "__")

expectedSchema := &v1.JSONSchemaProps{
Type: "object",
Properties: map[string]v1.JSONSchemaProps{
"spec__stringProperty": {
Type: "string",
},
"spec__intProperty": {
Type: "integer",
},
"spec__boolProperty": {
Type: "boolean",
},
"spec__nestedProperty__nestedStringProperty": {
Type: "string",
},
"spec__nestedProperty__nestedIntProperty": {
Type: "integer",
},
"spec__multiNestedProperty__nestedObjectProperty__nestedStringProperty": {
Type: "string",
},
},
Required: []string{"spec__stringProperty", "spec__nestedProperty__nestedStringProperty"},
}

if reflect.DeepEqual(shallowedSchema, expectedSchema) {
t.Logf("Shallowed schema is as expected")
} else {
t.Errorf("Shallowed schema is not as expected")
}
}

0 comments on commit 1b79c7c

Please sign in to comment.