From ea9872669a6dce5276966c4f11a39fc9d73c4122 Mon Sep 17 00:00:00 2001 From: quobix Date: Thu, 13 Jun 2024 06:53:58 -0400 Subject: [PATCH] Improving component path -> JSON path utils. --- utils/utils.go | 51 ++++++++++++++++++++++++++++++++++++++++----- utils/utils_test.go | 35 ++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index 552be370..a4eed08b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -588,7 +588,7 @@ func IsHttpVerb(verb string) bool { // define bracket name expression var ( - bracketNameExp = regexp.MustCompile(`^(\w+)\[(\w+)\]$`) + bracketNameExp = regexp.MustCompile(`^(\w+)\['?(\w+)\'?]$`) pathCharExp = regexp.MustCompile(`[%=;~.]`) ) @@ -631,6 +631,18 @@ func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", cleaned[len(cleaned)-1], segs[i]) continue } + + // if we have a plural parent, wrap it in quotes. + if i > 0 && segs[i-1][len(segs[i-1])-1] == 's' { + if i == 2 { // ignore first segment. + cleaned = append(cleaned, segs[i]) + continue + } + segs[i] = fmt.Sprintf("['%s']", segs[i]) + cleaned[len(cleaned)-1] = fmt.Sprintf("%s%s", cleaned[len(cleaned)-1], segs[i]) + continue + } + cleaned = append(cleaned, segs[i]) } } @@ -651,11 +663,40 @@ func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { } func ConvertComponentIdIntoPath(id string) (string, string) { - segs := strings.Split(id, "/") - name := segs[len(segs)-1] - return name, strings.ReplaceAll(fmt.Sprintf("%s.%s", - strings.Join(segs[:len(segs)-1], "."), name), "#", "$") + segs := strings.Split(id, ".") + name, _ := url.QueryUnescape(strings.ReplaceAll(segs[len(segs)-1], "~1", "/")) + var cleaned []string + + // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. + for i := range segs { + brackets := bracketNameExp.FindStringSubmatch(segs[i]) + if i == 0 { + if segs[i] == "$" { + cleaned = append(cleaned, "#") + continue + } + } + + if len(brackets) > 0 { + cleaned = append(cleaned[:i], append([]string{bracketNameExp.ReplaceAllString(segs[i], "$1/$2")}, cleaned[i:]...)...) + continue + } + cleaned = append(cleaned, segs[i]) + } + + if segs[0] != "#" { + cleaned = append(cleaned[:0], append([]string{"#"}, cleaned[0:]...)...) + + } + replaced := strings.ReplaceAll(strings.Join(cleaned, "/"), "$", "#") + + if len(replaced) > 0 { + if replaced[0] != '#' { + replaced = fmt.Sprintf("#%s", replaced) + } + } + return name, replaced } func RenderCodeSnippet(startNode *yaml.Node, specData []string, before, after int) string { diff --git a/utils/utils_test.go b/utils/utils_test.go index 7668a41c..9240eee3 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -697,7 +697,7 @@ func TestIsHttpVerb(t *testing.T) { func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) { segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/chicken/chips/pizza/cake") - assert.Equal(t, "$.chicken.chips.pizza.cake", path) + assert.Equal(t, "$.chicken.chips['pizza'].cake", path) assert.Equal(t, "cake", segment) } @@ -708,15 +708,23 @@ func TestConvertComponentIdIntoFriendlyPathSearch_SuperCrazy(t *testing.T) { } func TestConvertComponentIdIntoFriendlyPathSearch_Crazy(t *testing.T) { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/example/0/expires_at") - assert.Equal(t, "$.components.schemas.gpg-key.properties.subkeys.example[0].expires_at", path) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/examples/0/expires_at") + assert.Equal(t, "$.components.schemas['gpg-key'].properties['subkeys'].examples[0].expires_at", path) assert.Equal(t, "expires_at", segment) } func BenchmarkConvertComponentIdIntoFriendlyPathSearch_Crazy(t *testing.B) { for n := 0; n < t.N; n++ { - segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/example/0/expires_at") - assert.Equal(t, "$.components.schemas.gpg-key.properties.subkeys.example[0].expires_at", path) + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/examples/0/expires_at") + assert.Equal(t, "$.components.schemas.gpg-key.properties['subkeys'].examples[0].expires_at", path) + assert.Equal(t, "expires_at", segment) + } +} + +func BenchmarkConvertComponentIdIntoFriendlyPathSearch_Plural(t *testing.B) { + for n := 0; n < t.N; n++ { + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/gpg-key/properties/subkeys/examples/0/expires_at") + assert.Equal(t, "$.components.schemas['gpg-key'].properties['subkeys'].examples[0].expires_at", path) assert.Equal(t, "expires_at", segment) } } @@ -727,6 +735,12 @@ func TestConvertComponentIdIntoFriendlyPathSearch_Simple(t *testing.T) { assert.Equal(t, "get", segment) } +func TestConvertComponentIdIntoFriendlyPathSearch_Plural(t *testing.T) { + segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/components/schemas/FreshMan/properties/subkeys/examples/0/expires_at") + assert.Equal(t, "$.components.schemas['FreshMan'].properties['subkeys'].example[0].expires_at", path) + assert.Equal(t, "get", segment) +} + func TestConvertComponentIdIntoFriendlyPathSearch_Params(t *testing.T) { segment, path := ConvertComponentIdIntoFriendlyPathSearch("#/why/0") assert.Equal(t, "$.why[0]", path) @@ -781,6 +795,17 @@ func TestConvertComponentIdIntoPath(t *testing.T) { assert.Equal(t, "cake", segment) } +func TestConvertComponentIdIntoPath_Alt1(t *testing.T) { + segment, path := ConvertComponentIdIntoPath("$.chicken.chips['pizza'].cakes[0].burgers[2]") + assert.Equal(t, "#/chicken/chips/pizza/cakes/0/burgers/2", path) + assert.Equal(t, "burgers[2]", segment) +} + +func TestConvertComponentIdIntoPath_Alt2(t *testing.T) { + _, path := ConvertComponentIdIntoPath("chicken.chips['pizza'].cakes[0].burgers[2]") + assert.Equal(t, "#/chicken/chips/pizza/cakes/0/burgers/2", path) +} + func TestDetectCase(t *testing.T) { assert.Equal(t, PascalCase, DetectCase("PizzaPie")) assert.Equal(t, CamelCase, DetectCase("anyoneForTennis"))