fix: Clean up object.FieldPath

- Use string.Builder to be slightly more efficient
- Format invalid types with Go syntax, wrapped with brackets
- Test that invalid types don't panic
This commit is contained in:
Karl Isenberg 2022-01-07 18:27:57 -08:00
parent 476197d25b
commit a0743e6912
2 changed files with 23 additions and 8 deletions

View File

@ -6,6 +6,7 @@ package object
import (
"fmt"
"regexp"
"strings"
"k8s.io/apimachinery/pkg/util/validation/field"
)
@ -76,25 +77,30 @@ func NotFound(fieldPath []interface{}, value interface{}) *field.Error {
}
}
// Simplistic jsonpath formatter, just for NestedField errors.
// FieldPath formats a list of KRM field keys as a JSONPath expression.
// The only valid field keys in KRM are strings (map keys) and ints (list keys).
// Simple strings (see isSimpleString) will be delimited with a period.
// Complex strings will be wrapped with square brackets and double quotes.
// Integers will be wrapped with square brackets.
// All other types will be formatted best-effort within square brackets.
func FieldPath(fieldPath []interface{}) string {
path := ""
var sb strings.Builder
for _, field := range fieldPath {
switch typedField := field.(type) {
case string:
if isSimpleString(typedField) {
path += fmt.Sprintf(".%s", typedField)
_, _ = fmt.Fprintf(&sb, ".%s", typedField)
} else {
path += fmt.Sprintf("[%q]", typedField)
_, _ = fmt.Fprintf(&sb, "[%q]", typedField)
}
case int:
path += fmt.Sprintf("[%d]", typedField)
_, _ = fmt.Fprintf(&sb, "[%d]", typedField)
default:
// invalid. try anyway...
path += fmt.Sprintf(".%v", typedField)
// invalid type. try anyway...
_, _ = fmt.Fprintf(&sb, "[%#v]", typedField)
}
}
return path
return sb.String()
}
var simpleStringRegex = regexp.MustCompile(`^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$`)

View File

@ -78,6 +78,15 @@ func TestFieldPath(t *testing.T) {
fieldPath: []interface{}{"spec", "abc\n123"},
expected: `.spec["abc\n123"]`,
},
// result from invalid input doesn't matter, as long as it doesn't panic
"invalid type: float": {
fieldPath: []interface{}{"spec", float64(-1.0)},
expected: `.spec[-1]`,
},
"invalid type: struct": {
fieldPath: []interface{}{"spec", struct{ Field string }{Field: "value"}},
expected: `.spec[struct { Field string }{Field:"value"}]`,
},
}
for name, tc := range tests {