Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make to/from string methods private to the jsonschema package #942

Merged
merged 4 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libs/jsonschema/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (s *Schema) validatePattern(instance map[string]any) error {
if !ok {
continue
}
err := ValidatePatternMatch(k, v, fieldInfo)
err := validatePatternMatch(k, v, fieldInfo)
if err != nil {
return err
}
Expand Down
16 changes: 16 additions & 0 deletions libs/jsonschema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ type Schema struct {
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

const (
Expand Down
10 changes: 5 additions & 5 deletions libs/jsonschema/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
case BooleanType:
boolVal, ok := v.(bool)
Expand Down Expand Up @@ -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{}
for _, v := range arr {
s, err := ToString(v, T)
s, err := toString(v, T)
if err != nil {
return nil, err
}
Expand All @@ -84,7 +84,7 @@ func ToStringSlice(arr []any, T Type) ([]string, error) {
return res, nil
}

func FromString(s string, T Type) (any, error) {
func fromString(s string, T Type) (any, error) {
if T == StringType {
return s, nil
}
Expand Down Expand Up @@ -113,7 +113,7 @@ func FromString(s string, T Type) (any, error) {
return v, err
}

func ValidatePatternMatch(name string, value any, propertySchema *Schema) error {
func validatePatternMatch(name string, value any, propertySchema *Schema) error {
if propertySchema.Pattern == "" {
// Return early if no pattern is specified
return nil
Expand Down
54 changes: 27 additions & 27 deletions libs/jsonschema/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,82 +49,82 @@ func TestTemplateToInteger(t *testing.T) {
}

func TestTemplateToString(t *testing.T) {
s, err := ToString(true, BooleanType)
s, err := toString(true, BooleanType)
assert.NoError(t, err)
assert.Equal(t, "true", s)

s, err = ToString("abc", StringType)
s, err = toString("abc", StringType)
assert.NoError(t, err)
assert.Equal(t, "abc", s)

s, err = ToString(1.1, NumberType)
s, err = toString(1.1, NumberType)
assert.NoError(t, err)
assert.Equal(t, "1.1", s)

s, err = ToString(2, IntegerType)
s, err = toString(2, IntegerType)
assert.NoError(t, err)
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{}")

_, err = ToString("true", BooleanType)
_, err = toString("true", BooleanType)
assert.EqualError(t, err, "expected bool, got: \"true\"")

_, err = ToString(123, StringType)
_, err = toString(123, StringType)
assert.EqualError(t, err, "expected string, got: 123")

_, err = ToString(false, NumberType)
_, err = toString(false, NumberType)
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")

_, err = ToString("abc", "foobar")
_, err = toString("abc", "foobar")
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
}

func TestTemplateFromString(t *testing.T) {
v, err := FromString("true", BooleanType)
v, err := fromString("true", BooleanType)
assert.NoError(t, err)
assert.Equal(t, true, v)

v, err = FromString("abc", StringType)
v, err = fromString("abc", StringType)
assert.NoError(t, err)
assert.Equal(t, "abc", v)

v, err = FromString("1.1", NumberType)
v, err = fromString("1.1", NumberType)
assert.NoError(t, err)
// Floating point conversions are not perfect
assert.True(t, (v.(float64)-1.1) < 0.000001)

v, err = FromString("12345", IntegerType)
v, err = fromString("12345", IntegerType)
assert.NoError(t, err)
assert.Equal(t, int64(12345), v)

v, err = FromString("123", NumberType)
v, err = fromString("123", NumberType)
assert.NoError(t, err)
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\"")

_, 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")

_, 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")

_, err = FromString("1.0", "foobar")
_, err = fromString("1.0", "foobar")
assert.EqualError(t, err, "unknown json schema type: \"foobar\"")
}

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.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.Equal(t, []string{"1.1", "2.2", "3.3"}, s)
}
Expand All @@ -133,23 +133,23 @@ func TestValidatePropertyPatternMatch(t *testing.T) {
var err error

// 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)

// 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")

// 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`")

// 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)

// Expect custom error message on match fail
err = ValidatePatternMatch("bar", "axyze", &Schema{
err = validatePatternMatch("bar", "axyze", &Schema{
Type: "string",
Pattern: "(a*.d)",
Extension: Extension{
Expand All @@ -159,7 +159,7 @@ func TestValidatePropertyPatternMatch(t *testing.T) {
assert.EqualError(t, err, "invalid value for bar: \"axyze\". my custom msg")

// Expect generic message on match fail
err = ValidatePatternMatch("bar", "axyze", &Schema{
err = validatePatternMatch("bar", "axyze", &Schema{
Type: "string",
Pattern: "(a*.d)",
})
Expand Down
10 changes: 5 additions & 5 deletions libs/template/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ func (c *config) assignDefaultValues(r *renderer) error {
if property.Default == nil {
continue
}
defaultVal, err := jsonschema.ToString(property.Default, property.Type)
defaultVal, err := property.DefaultString()
if err != nil {
return err
}
defaultVal, err = r.executeTemplate(defaultVal)
if err != nil {
return err
}
defaultValTyped, err := jsonschema.FromString(defaultVal, property.Type)
defaultValTyped, err := property.ParseString(defaultVal)
if err != nil {
return err
}
Expand All @@ -107,7 +107,7 @@ func (c *config) promptForValues(r *renderer) error {
var defaultVal string
var err error
if property.Default != nil {
defaultValRaw, err := jsonschema.ToString(property.Default, property.Type)
defaultValRaw, err := property.DefaultString()
if err != nil {
return err
}
Expand All @@ -126,7 +126,7 @@ func (c *config) promptForValues(r *renderer) error {
var userInput string
if property.Enum != nil {
// convert list of enums to string slice
enums, err := jsonschema.ToStringSlice(property.Enum, property.Type)
enums, err := property.EnumStringSlice()
if err != nil {
return err
}
Expand All @@ -142,7 +142,7 @@ func (c *config) promptForValues(r *renderer) error {
}

// 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 {
return err
}
Expand Down