mirror of https://github.com/databricks/cli.git
Make to/from string methods private to the jsonschema package (#942)
## Changes This PR makes a few methods private, exposing cleaner interfaces to get the string representations for enums and default values of a JSON Schema. ## Tests Manually, template initialization for the `default-python` template still works as expected.
This commit is contained in:
parent
8e1156edbd
commit
fb32e78c9b
|
@ -122,7 +122,7 @@ func (s *Schema) validatePattern(instance map[string]any) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err := ValidatePatternMatch(k, v, fieldInfo)
|
err := validatePatternMatch(k, v, fieldInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,22 @@ type Schema struct {
|
||||||
Extension
|
Extension
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default value defined in a JSON Schema, represented as a string.
|
||||||
|
func (s *Schema) DefaultString() (string, error) {
|
||||||
|
return toString(s.Default, s.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allowed enum values defined in a JSON Schema, represented as a slice of strings.
|
||||||
|
func (s *Schema) EnumStringSlice() ([]string, error) {
|
||||||
|
return toStringSlice(s.Enum, s.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses a string as a Go primitive value. The type of the value is determined
|
||||||
|
// by the type defined in the JSON Schema.
|
||||||
|
func (s *Schema) ParseString(v string) (any, error) {
|
||||||
|
return fromString(v, s.Type)
|
||||||
|
}
|
||||||
|
|
||||||
type Type string
|
type Type string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -39,7 +39,7 @@ func toInteger(v any) (int64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToString(v any, T Type) (string, error) {
|
func toString(v any, T Type) (string, error) {
|
||||||
switch T {
|
switch T {
|
||||||
case BooleanType:
|
case BooleanType:
|
||||||
boolVal, ok := v.(bool)
|
boolVal, ok := v.(bool)
|
||||||
|
@ -72,10 +72,10 @@ func ToString(v any, T Type) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToStringSlice(arr []any, T Type) ([]string, error) {
|
func toStringSlice(arr []any, T Type) ([]string, error) {
|
||||||
res := []string{}
|
res := []string{}
|
||||||
for _, v := range arr {
|
for _, v := range arr {
|
||||||
s, err := ToString(v, T)
|
s, err := toString(v, T)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func ToStringSlice(arr []any, T Type) ([]string, error) {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromString(s string, T Type) (any, error) {
|
func fromString(s string, T Type) (any, error) {
|
||||||
if T == StringType {
|
if T == StringType {
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func FromString(s string, T Type) (any, error) {
|
||||||
return v, err
|
return v, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidatePatternMatch(name string, value any, propertySchema *Schema) error {
|
func validatePatternMatch(name string, value any, propertySchema *Schema) error {
|
||||||
if propertySchema.Pattern == "" {
|
if propertySchema.Pattern == "" {
|
||||||
// Return early if no pattern is specified
|
// Return early if no pattern is specified
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -49,82 +49,82 @@ func TestTemplateToInteger(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateToString(t *testing.T) {
|
func TestTemplateToString(t *testing.T) {
|
||||||
s, err := ToString(true, BooleanType)
|
s, err := toString(true, BooleanType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "true", s)
|
assert.Equal(t, "true", s)
|
||||||
|
|
||||||
s, err = ToString("abc", StringType)
|
s, err = toString("abc", StringType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "abc", s)
|
assert.Equal(t, "abc", s)
|
||||||
|
|
||||||
s, err = ToString(1.1, NumberType)
|
s, err = toString(1.1, NumberType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "1.1", s)
|
assert.Equal(t, "1.1", s)
|
||||||
|
|
||||||
s, err = ToString(2, IntegerType)
|
s, err = toString(2, IntegerType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "2", s)
|
assert.Equal(t, "2", s)
|
||||||
|
|
||||||
_, err = ToString([]string{}, ArrayType)
|
_, err = toString([]string{}, ArrayType)
|
||||||
assert.EqualError(t, err, "cannot format object of type array as a string. Value of object: []string{}")
|
assert.EqualError(t, err, "cannot format object of type array as a string. Value of object: []string{}")
|
||||||
|
|
||||||
_, err = ToString("true", BooleanType)
|
_, err = toString("true", BooleanType)
|
||||||
assert.EqualError(t, err, "expected bool, got: \"true\"")
|
assert.EqualError(t, err, "expected bool, got: \"true\"")
|
||||||
|
|
||||||
_, err = ToString(123, StringType)
|
_, err = toString(123, StringType)
|
||||||
assert.EqualError(t, err, "expected string, got: 123")
|
assert.EqualError(t, err, "expected string, got: 123")
|
||||||
|
|
||||||
_, err = ToString(false, NumberType)
|
_, err = toString(false, NumberType)
|
||||||
assert.EqualError(t, err, "expected float, got: false")
|
assert.EqualError(t, err, "expected float, got: false")
|
||||||
|
|
||||||
_, err = ToString("abc", IntegerType)
|
_, err = toString("abc", IntegerType)
|
||||||
assert.EqualError(t, err, "cannot convert \"abc\" to an integer")
|
assert.EqualError(t, err, "cannot convert \"abc\" to an integer")
|
||||||
|
|
||||||
_, err = ToString("abc", "foobar")
|
_, err = toString("abc", "foobar")
|
||||||
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
|
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateFromString(t *testing.T) {
|
func TestTemplateFromString(t *testing.T) {
|
||||||
v, err := FromString("true", BooleanType)
|
v, err := fromString("true", BooleanType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, true, v)
|
assert.Equal(t, true, v)
|
||||||
|
|
||||||
v, err = FromString("abc", StringType)
|
v, err = fromString("abc", StringType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "abc", v)
|
assert.Equal(t, "abc", v)
|
||||||
|
|
||||||
v, err = FromString("1.1", NumberType)
|
v, err = fromString("1.1", NumberType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
// Floating point conversions are not perfect
|
// Floating point conversions are not perfect
|
||||||
assert.True(t, (v.(float64)-1.1) < 0.000001)
|
assert.True(t, (v.(float64)-1.1) < 0.000001)
|
||||||
|
|
||||||
v, err = FromString("12345", IntegerType)
|
v, err = fromString("12345", IntegerType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, int64(12345), v)
|
assert.Equal(t, int64(12345), v)
|
||||||
|
|
||||||
v, err = FromString("123", NumberType)
|
v, err = fromString("123", NumberType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, float64(123), v)
|
assert.Equal(t, float64(123), v)
|
||||||
|
|
||||||
_, err = FromString("qrt", ArrayType)
|
_, err = fromString("qrt", ArrayType)
|
||||||
assert.EqualError(t, err, "cannot parse string as object of type array. Value of string: \"qrt\"")
|
assert.EqualError(t, err, "cannot parse string as object of type array. Value of string: \"qrt\"")
|
||||||
|
|
||||||
_, err = FromString("abc", IntegerType)
|
_, err = fromString("abc", IntegerType)
|
||||||
assert.EqualError(t, err, "could not parse \"abc\" as a integer: strconv.ParseInt: parsing \"abc\": invalid syntax")
|
assert.EqualError(t, err, "could not parse \"abc\" as a integer: strconv.ParseInt: parsing \"abc\": invalid syntax")
|
||||||
|
|
||||||
_, err = FromString("1.0", IntegerType)
|
_, err = fromString("1.0", IntegerType)
|
||||||
assert.EqualError(t, err, "could not parse \"1.0\" as a integer: strconv.ParseInt: parsing \"1.0\": invalid syntax")
|
assert.EqualError(t, err, "could not parse \"1.0\" as a integer: strconv.ParseInt: parsing \"1.0\": invalid syntax")
|
||||||
|
|
||||||
_, err = FromString("1.0", "foobar")
|
_, err = fromString("1.0", "foobar")
|
||||||
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
|
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTemplateToStringSlice(t *testing.T) {
|
func TestTemplateToStringSlice(t *testing.T) {
|
||||||
s, err := ToStringSlice([]any{"a", "b", "c"}, StringType)
|
s, err := toStringSlice([]any{"a", "b", "c"}, StringType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, []string{"a", "b", "c"}, s)
|
assert.Equal(t, []string{"a", "b", "c"}, s)
|
||||||
|
|
||||||
s, err = ToStringSlice([]any{1.1, 2.2, 3.3}, NumberType)
|
s, err = toStringSlice([]any{1.1, 2.2, 3.3}, NumberType)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, []string{"1.1", "2.2", "3.3"}, s)
|
assert.Equal(t, []string{"1.1", "2.2", "3.3"}, s)
|
||||||
}
|
}
|
||||||
|
@ -133,23 +133,23 @@ func TestValidatePropertyPatternMatch(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Expect no error if no pattern is specified.
|
// Expect no error if no pattern is specified.
|
||||||
err = ValidatePatternMatch("foo", 1, &Schema{Type: "integer"})
|
err = validatePatternMatch("foo", 1, &Schema{Type: "integer"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Expect error because value is not a string.
|
// Expect error because value is not a string.
|
||||||
err = ValidatePatternMatch("bar", 1, &Schema{Type: "integer", Pattern: "abc"})
|
err = validatePatternMatch("bar", 1, &Schema{Type: "integer", Pattern: "abc"})
|
||||||
assert.EqualError(t, err, "invalid value for bar: 1. Expected a value of type string")
|
assert.EqualError(t, err, "invalid value for bar: 1. Expected a value of type string")
|
||||||
|
|
||||||
// Expect error because the pattern is invalid.
|
// Expect error because the pattern is invalid.
|
||||||
err = ValidatePatternMatch("bar", "xyz", &Schema{Type: "string", Pattern: "(abc"})
|
err = validatePatternMatch("bar", "xyz", &Schema{Type: "string", Pattern: "(abc"})
|
||||||
assert.EqualError(t, err, "error parsing regexp: missing closing ): `(abc`")
|
assert.EqualError(t, err, "error parsing regexp: missing closing ): `(abc`")
|
||||||
|
|
||||||
// Expect no error because the pattern matches.
|
// Expect no error because the pattern matches.
|
||||||
err = ValidatePatternMatch("bar", "axyzd", &Schema{Type: "string", Pattern: "(a*.d)"})
|
err = validatePatternMatch("bar", "axyzd", &Schema{Type: "string", Pattern: "(a*.d)"})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Expect custom error message on match fail
|
// Expect custom error message on match fail
|
||||||
err = ValidatePatternMatch("bar", "axyze", &Schema{
|
err = validatePatternMatch("bar", "axyze", &Schema{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "(a*.d)",
|
Pattern: "(a*.d)",
|
||||||
Extension: Extension{
|
Extension: Extension{
|
||||||
|
@ -159,7 +159,7 @@ func TestValidatePropertyPatternMatch(t *testing.T) {
|
||||||
assert.EqualError(t, err, "invalid value for bar: \"axyze\". my custom msg")
|
assert.EqualError(t, err, "invalid value for bar: \"axyze\". my custom msg")
|
||||||
|
|
||||||
// Expect generic message on match fail
|
// Expect generic message on match fail
|
||||||
err = ValidatePatternMatch("bar", "axyze", &Schema{
|
err = validatePatternMatch("bar", "axyze", &Schema{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Pattern: "(a*.d)",
|
Pattern: "(a*.d)",
|
||||||
})
|
})
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (c *config) assignDefaultValues(r *renderer) error {
|
||||||
if property.Default == nil {
|
if property.Default == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
defaultVal, err := jsonschema.ToString(property.Default, property.Type)
|
defaultVal, err := property.DefaultString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func (c *config) assignDefaultValues(r *renderer) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defaultValTyped, err := jsonschema.FromString(defaultVal, property.Type)
|
defaultValTyped, err := property.ParseString(defaultVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (c *config) promptForValues(r *renderer) error {
|
||||||
var defaultVal string
|
var defaultVal string
|
||||||
var err error
|
var err error
|
||||||
if property.Default != nil {
|
if property.Default != nil {
|
||||||
defaultValRaw, err := jsonschema.ToString(property.Default, property.Type)
|
defaultValRaw, err := property.DefaultString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func (c *config) promptForValues(r *renderer) error {
|
||||||
var userInput string
|
var userInput string
|
||||||
if property.Enum != nil {
|
if property.Enum != nil {
|
||||||
// convert list of enums to string slice
|
// convert list of enums to string slice
|
||||||
enums, err := jsonschema.ToStringSlice(property.Enum, property.Type)
|
enums, err := property.EnumStringSlice()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ func (c *config) promptForValues(r *renderer) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert user input string back to a value
|
// Convert user input string back to a value
|
||||||
c.values[name], err = jsonschema.FromString(userInput, property.Type)
|
c.values[name], err = property.ParseString(userInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue