Skip to content

Commit

Permalink
feat(blueprint-test): add GetJSONPaths (#2705)
Browse files Browse the repository at this point in the history
  • Loading branch information
apeabody authored Nov 20, 2024
1 parent a548c1d commit 53eeec7
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 0 deletions.
95 changes: 95 additions & 0 deletions infra/blueprint-test/pkg/utils/jsonpaths.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package utils

import (
"slices"
"strconv"
"strings"

"github.com/tidwall/gjson"
)

// GetJSONPaths returns a []string of all possible JSON paths for a gjson.Result
func GetJSONPaths(result gjson.Result) []string {
return getJSONPaths(result.Value(), []string{})
}

func getJSONPaths(item interface{}, crumbs []string) []string {
var paths []string

switch val := item.(type) {
case []interface{}:
for i, v := range val {
// Add this item to paths
paths = append(paths, strings.Join(append(crumbs, strconv.Itoa(i)), "."))
// Search child items
paths = append(paths,
getJSONPaths(v, append(crumbs, strconv.Itoa(i)))...,
)
}
case map[string]interface{}:
for k, v := range val {
// Add this item to paths
paths = append(paths, strings.Join(append(crumbs, k), "."))
// Search child items
paths = append(paths,
getJSONPaths(v, append(crumbs, k))...,
)

}
}

slices.Sort(paths)
return paths
}

// GetTerminalJSONPaths returns a []string of all terminal JSON paths for a gjson.Result
func GetTerminalJSONPaths(result gjson.Result) []string {
return getTerminalJSONPaths(result.Value(), []string{})
}

func getTerminalJSONPaths(item interface{}, crumbs []string) []string {
var paths []string

// Only add paths for JSON bool, number, string, and null
switch val := item.(type) {
case bool:
return []string{strings.Join(crumbs, ".")}
case float64:
return []string{strings.Join(crumbs, ".")}
case string:
return []string{strings.Join(crumbs, ".")}
case nil:
return []string{strings.Join(crumbs, ".")}
case []interface{}:
for i, v := range val {
paths = append(paths,
getTerminalJSONPaths(v, append(crumbs, strconv.Itoa(i)))...,
)
}
case map[string]interface{}:
for k, v := range val {
paths = append(paths,
getTerminalJSONPaths(v, append(crumbs, k))...,
)
}
}

slices.Sort(paths)
return paths
}
92 changes: 92 additions & 0 deletions infra/blueprint-test/pkg/utils/jsonpaths_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package utils

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
)

func TestGetJSONPaths(t *testing.T) {
tests := []struct {
name string
json gjson.Result
paths []string
}{
{
name: "one",
json: gjson.Parse(`{
"apiVersion": "v1",
"autopilot": {},
"locations": [
"europe-west4-b"
],
"metadata": {
"annotations": [
{"my-annotation": "test"}
]
},
"bool": true,
"number": 3,
"null": null,
}`),
paths: []string{"apiVersion", "autopilot", "bool", "locations", "locations.0", "metadata", "metadata.annotations", "metadata.annotations.0", "metadata.annotations.0.my-annotation", "null", "number"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tt.paths, GetJSONPaths(tt.json))
})
}
}

func TestTerminalGetJSONPaths(t *testing.T) {
tests := []struct {
name string
json gjson.Result
paths []string
}{
{
name: "one",
json: gjson.Parse(`{
"apiVersion": "v1",
"autopilot": {},
"locations": [
"europe-west4-b"
],
"metadata": {
"annotations": [
{"my-annotation": "test"}
]
},
"bool": true,
"number": 3,
"null": null,
}`),
paths: []string{"apiVersion", "bool", "locations.0", "metadata.annotations.0.my-annotation", "null", "number"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tt.paths, GetTerminalJSONPaths(tt.json))
})
}
}

0 comments on commit 53eeec7

Please sign in to comment.