From 1b79c7c4f8c7ac6549571487a698dab9b6047a9a Mon Sep 17 00:00:00 2001 From: danielsinai Date: Fri, 17 May 2024 19:02:20 +0300 Subject: [PATCH] fix: added tests and handle required recursively --- pkg/crd/crd.go | 28 +------------ pkg/crd/crd_test.go | 59 --------------------------- pkg/crd/utils.go | 62 +++++++++++++++++++++++++++++ pkg/crd/utils_test.go | 92 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 85 deletions(-) create mode 100644 pkg/crd/utils.go create mode 100644 pkg/crd/utils_test.go diff --git a/pkg/crd/crd.go b/pkg/crd/crd.go index 37f9a0f..44a5d68 100644 --- a/pkg/crd/crd.go +++ b/pkg/crd/crd.go @@ -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) } @@ -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) diff --git a/pkg/crd/crd_test.go b/pkg/crd/crd_test.go index 58cc014..5bbb4f8 100644 --- a/pkg/crd/crd_test.go +++ b/pkg/crd/crd_test.go @@ -1,7 +1,6 @@ package crd import ( - "fmt" "slices" "testing" @@ -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) -} diff --git a/pkg/crd/utils.go b/pkg/crd/utils.go new file mode 100644 index 0000000..dee1a48 --- /dev/null +++ b/pkg/crd/utils.go @@ -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 +} diff --git a/pkg/crd/utils_test.go b/pkg/crd/utils_test.go new file mode 100644 index 0000000..32323f0 --- /dev/null +++ b/pkg/crd/utils_test.go @@ -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") + } +}