From 04946d268a9268203ad4c619747a92a7d1c9ddda Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Fri, 19 Jan 2024 01:21:24 -0800 Subject: [PATCH 01/19] Add AnyOf Support to SkipPromptIf --- libs/jsonschema/schema.go | 3 ++ libs/template/config.go | 31 +++++++++++-- libs/template/config_test.go | 86 ++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/libs/jsonschema/schema.go b/libs/jsonschema/schema.go index 443e7af6e6..967e2e9cdc 100644 --- a/libs/jsonschema/schema.go +++ b/libs/jsonschema/schema.go @@ -60,6 +60,9 @@ type Schema struct { // Extension embeds our custom JSON schema extensions. Extension + + // Schema that must match any of the schemas in the array + AnyOf []*Schema `json:"anyOf,omitempty"` } // Default value defined in a JSON Schema, represented as a string. diff --git a/libs/template/config.go b/libs/template/config.go index 14e09fe560..71c9cd52d0 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -127,11 +127,34 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { // Check if conditions specified by template author for skipping the prompt // are satisfied. If they are not, we have to prompt for a user input. - for name, property := range p.Schema.SkipPromptIf.Properties { - if v, ok := c.values[name]; ok && v == property.Const { - continue + if p.Schema.SkipPromptIf.Properties != nil { + for name, property := range p.Schema.SkipPromptIf.Properties { + if v, ok := c.values[name]; ok && v == property.Const { + continue + } + return false, nil + } + } + + // Check if AnyOf is set first. If so, then iterate over all property sets to find a match. + // If no match is found (allFalse stays true), we have to prompt for a user input. + if p.Schema.SkipPromptIf.AnyOf != nil { + allFalse := true + for _, properties := range p.Schema.SkipPromptIf.AnyOf { + match := true + for name, property := range properties.Properties { + if v, ok := c.values[name]; ok && v == property.Const { + continue + } + match = false + } + if match { + allFalse = false + } + } + if allFalse { + return false, nil } - return false, nil } if p.Schema.Default == nil { diff --git a/libs/template/config_test.go b/libs/template/config_test.go index c4968ee1a1..a02c80b503 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -398,3 +398,89 @@ func TestPromptIsSkipped(t *testing.T) { assert.True(t, skip) assert.Equal(t, "hello-world", c.values["xyz"]) } + +func TestPromptIsSkippedAnyOf(t *testing.T) { + c := config{ + ctx: context.Background(), + values: make(map[string]any), + schema: &jsonschema.Schema{ + Properties: map[string]*jsonschema.Schema{ + "abc": { + Type: "string", + }, + "def": { + Type: "integer", + }, + "xyz": { + Type: "string", + Default: "hello-world", + Extension: jsonschema.Extension{ + SkipPromptIf: &jsonschema.Schema{ + AnyOf: []*jsonschema.Schema{ + { + Properties: map[string]*jsonschema.Schema{ + "abc": { + Const: "foobar", + }, + "def": { + Const: 123, + }, + }, + }, + { + Properties: map[string]*jsonschema.Schema{ + "abc": { + Const: "barfoo", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + // No skip condition defined. Prompt should not be skipped. + skip, err := c.skipPrompt(jsonschema.Property{ + Name: "abc", + Schema: c.schema.Properties["abc"], + }, testRenderer()) + assert.NoError(t, err) + assert.False(t, skip) + + // Values do not match skip condition. Prompt should not be skipped. + c.values["abc"] = "foobar" + c.values["def"] = 1234 + skip, err = c.skipPrompt(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + }, testRenderer()) + assert.NoError(t, err) + assert.False(t, skip) + assert.NotContains(t, c.values, "xyz") + + // Values match skip condition. Prompt should be skipped. Default value should + // be assigned to "xyz". + c.values["abc"] = "foobar" + c.values["def"] = 123 + skip, err = c.skipPrompt(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + }, testRenderer()) + assert.NoError(t, err) + assert.True(t, skip) + assert.Equal(t, "hello-world", c.values["xyz"]) + + // Values match skip condition. Prompt should be skipped. Default value should + // be assigned to "xyz". + c.values["abc"] = "barfoo" + skip, err = c.skipPrompt(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + }, testRenderer()) + assert.NoError(t, err) + assert.True(t, skip) + assert.Equal(t, "hello-world", c.values["xyz"]) +} From 6a8c0524dda2ec2626dac815f0900f20b86a3f5d Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Fri, 19 Jan 2024 01:26:25 -0800 Subject: [PATCH 02/19] Change double negation to single check --- libs/template/config.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/template/config.go b/libs/template/config.go index 71c9cd52d0..bfb01d309d 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -143,10 +143,9 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { for _, properties := range p.Schema.SkipPromptIf.AnyOf { match := true for name, property := range properties.Properties { - if v, ok := c.values[name]; ok && v == property.Const { - continue + if v, ok := c.values[name]; !ok || v != property.Const { + match = false } - match = false } if match { allFalse = false From 5a37d661a73a2ae08956f36a47b8263c6fba44c2 Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Fri, 19 Jan 2024 16:51:34 -0800 Subject: [PATCH 03/19] Refactor --- libs/jsonschema/instance.go | 31 +++++++ libs/jsonschema/instance_test.go | 89 +++++++++++++++++++ .../instance-validate/test-schema-anyof.json | 24 +++++ .../instance-validate/test-schema-const.json | 12 +++ libs/template/config.go | 32 +------ 5 files changed, 159 insertions(+), 29 deletions(-) create mode 100644 libs/jsonschema/testdata/instance-validate/test-schema-anyof.json create mode 100644 libs/jsonschema/testdata/instance-validate/test-schema-const.json diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 0b060cfff4..da7453ec57 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -47,6 +47,8 @@ func (s *Schema) ValidateInstance(instance map[string]any) error { s.validateRequired, s.validateTypes, s.validatePattern, + s.validateConst, + s.validateAnyOf, } for _, fn := range validations { @@ -129,3 +131,32 @@ func (s *Schema) validatePattern(instance map[string]any) error { } return nil } + +func (s *Schema) validateConst(instance map[string]any) error { + for name, property := range s.Properties { + if property.Const == nil { + continue + } + v, ok := instance[name] + if !ok { + return fmt.Errorf("property %s has const set to %v but no value was provided", name, property.Const) + } + if v != property.Const { + return fmt.Errorf("expected value of property %s to be %v. Found: %v", name, property.Const, v) + } + } + return nil +} + +func (s *Schema) validateAnyOf(instance map[string]any) error { + if s.AnyOf == nil { + return nil + } + for _, anyOf := range s.AnyOf { + err := anyOf.validateConst(instance) + if err == nil { + return nil + } + } + return fmt.Errorf("instance does not match any of the schemas in anyOf") +} diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index 8edbf796de..9a2c804848 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -222,3 +222,92 @@ func TestValidateInstanceForMultiplePatterns(t *testing.T) { assert.EqualError(t, schema.validatePattern(invalidInstanceValue), "invalid value for bar: \"xyz\". Expected to match regex pattern: ^[d-f]+$") assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "invalid value for bar: \"xyz\". Expected to match regex pattern: ^[d-f]+$") } + +func TestValidateInstanceForConst(t *testing.T) { + schema, err := Load("./testdata/instance-validate/test-schema-const.json") + require.NoError(t, err) + + // Valid values for both foo and bar + validInstance := map[string]any{ + "foo": "abc", + "bar": "def", + } + assert.NoError(t, schema.validateConst(validInstance)) + assert.NoError(t, schema.ValidateInstance(validInstance)) + + // Empty instance + emptyInstanceValue := map[string]any{} + assert.ErrorContains(t, schema.validateConst(emptyInstanceValue), "but no value was provided") + assert.ErrorContains(t, schema.ValidateInstance(emptyInstanceValue), "but no value was provided") + + // Missing value for bar + missingInstanceValue := map[string]any{ + "foo": "abc", + } + assert.EqualError(t, schema.validateConst(missingInstanceValue), "property bar has const set to def but no value was provided") + assert.EqualError(t, schema.ValidateInstance(missingInstanceValue), "property bar has const set to def but no value was provided") + + // Valid value for bar, invalid value for foo + invalidInstanceValue := map[string]any{ + "foo": "xyz", + "bar": "def", + } + assert.EqualError(t, schema.validateConst(invalidInstanceValue), "expected value of property foo to be abc. Found: xyz") + assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "expected value of property foo to be abc. Found: xyz") + + // Valid value for foo, invalid value for bar + invalidInstanceValue = map[string]any{ + "foo": "abc", + "bar": "xyz", + } + assert.EqualError(t, schema.validateConst(invalidInstanceValue), "expected value of property bar to be def. Found: xyz") + assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "expected value of property bar to be def. Found: xyz") +} + +func TestValidateInstanceForAnyOf(t *testing.T) { + schema, err := Load("./testdata/instance-validate/test-schema-anyof.json") + require.NoError(t, err) + + // Valid values for both foo and bar + validInstance := map[string]any{ + "foo": "abc", + "bar": "abc", + } + assert.NoError(t, schema.validateAnyOf(validInstance)) + assert.NoError(t, schema.ValidateInstance(validInstance)) + + // Valid values for bar + validInstance = map[string]any{ + "foo": "abc", + "bar": "def", + } + assert.NoError(t, schema.validateAnyOf(validInstance)) + assert.NoError(t, schema.ValidateInstance(validInstance)) + + // Empty instance + emptyInstanceValue := map[string]any{} + assert.EqualError(t, schema.validateAnyOf(emptyInstanceValue), "instance does not match any of the schemas in anyOf") + assert.EqualError(t, schema.ValidateInstance(emptyInstanceValue), "instance does not match any of the schemas in anyOf") + + // Missing values for bar, invalid value for foo + missingInstanceValue := map[string]any{ + "foo": "xyz", + } + assert.EqualError(t, schema.validateAnyOf(missingInstanceValue), "instance does not match any of the schemas in anyOf") + assert.EqualError(t, schema.ValidateInstance(missingInstanceValue), "instance does not match any of the schemas in anyOf") + + // Valid value for bar, invalid value for foo + invalidInstanceValue := map[string]any{ + "foo": "xyz", + "bar": "abc", + } + assert.EqualError(t, schema.validateAnyOf(invalidInstanceValue), "instance does not match any of the schemas in anyOf") + assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "instance does not match any of the schemas in anyOf") + + // Invalid value for both + invalidInstanceValue = map[string]any{ + "bar": "xyz", + } + assert.EqualError(t, schema.validateAnyOf(invalidInstanceValue), "instance does not match any of the schemas in anyOf") + assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "instance does not match any of the schemas in anyOf") +} diff --git a/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json b/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json new file mode 100644 index 0000000000..0f58b9ada7 --- /dev/null +++ b/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json @@ -0,0 +1,24 @@ +{ + "anyOf": [ + { + "properties": { + "foo": { + "type": "string", + "const": "abc" + }, + "bar": { + "type": "string", + "const": "abc" + } + } + }, + { + "properties": { + "bar": { + "type": "string", + "const": "def" + } + } + } + ] +} diff --git a/libs/jsonschema/testdata/instance-validate/test-schema-const.json b/libs/jsonschema/testdata/instance-validate/test-schema-const.json new file mode 100644 index 0000000000..3d6091435e --- /dev/null +++ b/libs/jsonschema/testdata/instance-validate/test-schema-const.json @@ -0,0 +1,12 @@ +{ + "properties": { + "foo": { + "type": "string", + "const": "abc" + }, + "bar": { + "type": "string", + "const": "def" + } + } +} diff --git a/libs/template/config.go b/libs/template/config.go index bfb01d309d..fff26453e5 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -125,35 +125,9 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { return false, nil } - // Check if conditions specified by template author for skipping the prompt - // are satisfied. If they are not, we have to prompt for a user input. - if p.Schema.SkipPromptIf.Properties != nil { - for name, property := range p.Schema.SkipPromptIf.Properties { - if v, ok := c.values[name]; ok && v == property.Const { - continue - } - return false, nil - } - } - - // Check if AnyOf is set first. If so, then iterate over all property sets to find a match. - // If no match is found (allFalse stays true), we have to prompt for a user input. - if p.Schema.SkipPromptIf.AnyOf != nil { - allFalse := true - for _, properties := range p.Schema.SkipPromptIf.AnyOf { - match := true - for name, property := range properties.Properties { - if v, ok := c.values[name]; !ok || v != property.Const { - match = false - } - } - if match { - allFalse = false - } - } - if allFalse { - return false, nil - } + validationErr := p.Schema.SkipPromptIf.ValidateInstance(c.values) + if validationErr != nil { + return false, nil } if p.Schema.Default == nil { From f31191fd2a6e23ae02b0af462b0abae66db02a91 Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Sun, 21 Jan 2024 22:52:31 -0800 Subject: [PATCH 04/19] Apply comments --- libs/jsonschema/instance.go | 10 ++++++---- libs/jsonschema/instance_test.go | 16 ++++++++-------- libs/template/config_test.go | 10 ++++++++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index da7453ec57..5ae554e7b6 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -138,20 +138,22 @@ func (s *Schema) validateConst(instance map[string]any) error { continue } v, ok := instance[name] - if !ok { - return fmt.Errorf("property %s has const set to %v but no value was provided", name, property.Const) - } - if v != property.Const { + if ok && v != property.Const { return fmt.Errorf("expected value of property %s to be %v. Found: %v", name, property.Const, v) } } return nil } +// Validates that the instance matches at least one of the schemas in anyOf +// but will also succeed if the property values are omitted. +// For more information, see https://json-schema.org/understanding-json-schema/reference/combining#anyof. func (s *Schema) validateAnyOf(instance map[string]any) error { if s.AnyOf == nil { return nil } + // Currently, we only validate const for anyOf schemas since anyOf is + // only used by skip_prompt_if, which only supports const. for _, anyOf := range s.AnyOf { err := anyOf.validateConst(instance) if err == nil { diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index 9a2c804848..14e555646b 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -237,15 +237,15 @@ func TestValidateInstanceForConst(t *testing.T) { // Empty instance emptyInstanceValue := map[string]any{} - assert.ErrorContains(t, schema.validateConst(emptyInstanceValue), "but no value was provided") - assert.ErrorContains(t, schema.ValidateInstance(emptyInstanceValue), "but no value was provided") + assert.NoError(t, schema.validateConst(emptyInstanceValue)) + assert.NoError(t, schema.ValidateInstance(emptyInstanceValue)) // Missing value for bar missingInstanceValue := map[string]any{ "foo": "abc", } - assert.EqualError(t, schema.validateConst(missingInstanceValue), "property bar has const set to def but no value was provided") - assert.EqualError(t, schema.ValidateInstance(missingInstanceValue), "property bar has const set to def but no value was provided") + assert.NoError(t, schema.validateConst(missingInstanceValue)) + assert.NoError(t, schema.ValidateInstance(missingInstanceValue)) // Valid value for bar, invalid value for foo invalidInstanceValue := map[string]any{ @@ -286,15 +286,15 @@ func TestValidateInstanceForAnyOf(t *testing.T) { // Empty instance emptyInstanceValue := map[string]any{} - assert.EqualError(t, schema.validateAnyOf(emptyInstanceValue), "instance does not match any of the schemas in anyOf") - assert.EqualError(t, schema.ValidateInstance(emptyInstanceValue), "instance does not match any of the schemas in anyOf") + assert.NoError(t, schema.validateAnyOf(emptyInstanceValue)) + assert.NoError(t, schema.ValidateInstance(emptyInstanceValue)) // Missing values for bar, invalid value for foo missingInstanceValue := map[string]any{ "foo": "xyz", } - assert.EqualError(t, schema.validateAnyOf(missingInstanceValue), "instance does not match any of the schemas in anyOf") - assert.EqualError(t, schema.ValidateInstance(missingInstanceValue), "instance does not match any of the schemas in anyOf") + assert.NoError(t, schema.validateAnyOf(missingInstanceValue)) + assert.NoError(t, schema.ValidateInstance(missingInstanceValue)) // Valid value for bar, invalid value for foo invalidInstanceValue := map[string]any{ diff --git a/libs/template/config_test.go b/libs/template/config_test.go index a02c80b503..29791aa90a 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -461,6 +461,16 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.False(t, skip) assert.NotContains(t, c.values, "xyz") + // Missing values. Prompt should not be skipped. + c.values["abc"] = "foobar" + skip, err = c.skipPrompt(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + }, testRenderer()) + assert.NoError(t, err) + assert.False(t, skip) + assert.NotContains(t, c.values, "xyz") + // Values match skip condition. Prompt should be skipped. Default value should // be assigned to "xyz". c.values["abc"] = "foobar" From 699f7dd527f6fe77e83f4aebac71ead5b484229a Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Mon, 22 Jan 2024 16:25:13 -0800 Subject: [PATCH 05/19] Add required condition --- libs/jsonschema/instance.go | 13 ++++++++----- libs/template/config.go | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 5ae554e7b6..6acd139b5d 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -133,13 +133,16 @@ func (s *Schema) validatePattern(instance map[string]any) error { } func (s *Schema) validateConst(instance map[string]any) error { - for name, property := range s.Properties { - if property.Const == nil { + for k, v := range instance { + fieldInfo, ok := s.Properties[k] + if !ok { + continue + } + if fieldInfo.Const == nil { continue } - v, ok := instance[name] - if ok && v != property.Const { - return fmt.Errorf("expected value of property %s to be %v. Found: %v", name, property.Const, v) + if v != fieldInfo.Const { + return fmt.Errorf("expected value of property %s to be %v. Found: %v", k, fieldInfo.Const, v) } } return nil diff --git a/libs/template/config.go b/libs/template/config.go index fff26453e5..f23fef605c 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -125,6 +125,12 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { return false, nil } + var keys []string + for k := range p.Schema.SkipPromptIf.Properties { + keys = append(keys, k) + } + p.Schema.SkipPromptIf.Required = append(keys, p.Schema.SkipPromptIf.Required...) + validationErr := p.Schema.SkipPromptIf.ValidateInstance(c.values) if validationErr != nil { return false, nil From 769d0921086ee5ca42d59890d59e512d8c6ec259 Mon Sep 17 00:00:00 2001 From: Arpit Jasapara Date: Tue, 23 Jan 2024 21:58:50 -0800 Subject: [PATCH 06/19] apply comments --- libs/jsonschema/instance.go | 15 +++------------ libs/template/config.go | 2 ++ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 6acd139b5d..5bcbcc0aa0 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -24,10 +24,7 @@ func (s *Schema) LoadInstance(path string) (map[string]any, error) { // We convert integer properties from float64 to int64 here. for name, v := range instance { propertySchema, ok := s.Properties[name] - if !ok { - continue - } - if propertySchema.Type != IntegerType { + if !ok || propertySchema.Type != IntegerType { continue } integerValue, err := toInteger(v) @@ -105,10 +102,7 @@ func (s *Schema) validateTypes(instance map[string]any) error { func (s *Schema) validateEnum(instance map[string]any) error { for k, v := range instance { fieldInfo, ok := s.Properties[k] - if !ok { - continue - } - if fieldInfo.Enum == nil { + if !ok || fieldInfo.Enum == nil { continue } if !slices.Contains(fieldInfo.Enum, v) { @@ -135,10 +129,7 @@ func (s *Schema) validatePattern(instance map[string]any) error { func (s *Schema) validateConst(instance map[string]any) error { for k, v := range instance { fieldInfo, ok := s.Properties[k] - if !ok { - continue - } - if fieldInfo.Const == nil { + if !ok || fieldInfo.Const == nil { continue } if v != fieldInfo.Const { diff --git a/libs/template/config.go b/libs/template/config.go index f23fef605c..4173c4cd99 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -125,6 +125,8 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { return false, nil } + // All fields referred to in a SkipPromptIf condition are implicitly made required and + // we diverge from strictly following the JSON schema because it makes the author UX better. var keys []string for k := range p.Schema.SkipPromptIf.Properties { keys = append(keys, k) From 80cf44b691ce1a82405d27bd9c7f91b8f0c48f8f Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 12:10:55 +0530 Subject: [PATCH 07/19] Add anyOf props as required and error when required fields are not specified --- libs/jsonschema/instance.go | 10 +++++++++- libs/template/config.go | 20 +++++++++++++++++--- libs/template/config_test.go | 27 +++++++++++++++++---------- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 5bcbcc0aa0..cc33c42a6c 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -73,12 +73,20 @@ func (s *Schema) validateAdditionalProperties(instance map[string]any) error { return nil } +type RequiredPropertyMissingError struct { + Name string +} + +func (err RequiredPropertyMissingError) Error() string { + return fmt.Sprintf("no value provided for required property %s", err.Name) +} + // This function validates that all require properties in the schema have values // in the instance. func (s *Schema) validateRequired(instance map[string]any) error { for _, name := range s.Required { if _, ok := instance[name]; !ok { - return fmt.Errorf("no value provided for required property %s", name) + return RequiredPropertyMissingError{Name: name} } } return nil diff --git a/libs/template/config.go b/libs/template/config.go index 4173c4cd99..fd07023285 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -127,13 +127,27 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { // All fields referred to in a SkipPromptIf condition are implicitly made required and // we diverge from strictly following the JSON schema because it makes the author UX better. - var keys []string + required := make(map[string]struct{}) + for _, k := range p.Schema.SkipPromptIf.Required { + required[k] = struct{}{} + } for k := range p.Schema.SkipPromptIf.Properties { - keys = append(keys, k) + required[k] = struct{}{} + } + for _, schema := range p.Schema.SkipPromptIf.AnyOf { + for k := range schema.Properties { + required[k] = struct{}{} + } } - p.Schema.SkipPromptIf.Required = append(keys, p.Schema.SkipPromptIf.Required...) + p.Schema.SkipPromptIf.Required = maps.Keys(required) + // Validate the partial config against skip_prompt_if schema validationErr := p.Schema.SkipPromptIf.ValidateInstance(c.values) + + target := jsonschema.RequiredPropertyMissingError{} + if errors.As(validationErr, &target) { + return false, fmt.Errorf("property %s is used in skip_prompt_if but has no value assigned", target.Name) + } if validationErr != nil { return false, nil } diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 29791aa90a..9db63ae888 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -451,8 +451,10 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.False(t, skip) // Values do not match skip condition. Prompt should not be skipped. - c.values["abc"] = "foobar" - c.values["def"] = 1234 + c.values = map[string]any{ + "abc": "foobar", + "def": 1234, + } skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], @@ -462,19 +464,21 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.NotContains(t, c.values, "xyz") // Missing values. Prompt should not be skipped. - c.values["abc"] = "foobar" - skip, err = c.skipPrompt(jsonschema.Property{ + c.values = map[string]any{ + "abc": "foobar", + } + _, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) - assert.NoError(t, err) - assert.False(t, skip) - assert.NotContains(t, c.values, "xyz") + assert.ErrorContains(t, err, "property def is used in skip_prompt_if but has no value assigned") // Values match skip condition. Prompt should be skipped. Default value should // be assigned to "xyz". - c.values["abc"] = "foobar" - c.values["def"] = 123 + c.values = map[string]any{ + "abc": "foobar", + "def": 123, + } skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], @@ -485,7 +489,10 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { // Values match skip condition. Prompt should be skipped. Default value should // be assigned to "xyz". - c.values["abc"] = "barfoo" + c.values = map[string]any{ + "abc": "barfoo", + "def": 0, + } skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], From 80156a7a46fcade32c6442d812d37d1fa811ac8c Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 12:22:21 +0530 Subject: [PATCH 08/19] fix test --- libs/template/config_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 9db63ae888..e0c49d026e 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -3,6 +3,7 @@ package template import ( "context" "fmt" + "strings" "testing" "text/template" @@ -355,14 +356,14 @@ func TestPromptIsSkipped(t *testing.T) { assert.NoError(t, err) assert.False(t, skip) - // No values assigned to config. Prompt should not be skipped. + // No values assigned to config. Expect error because required values are + // missing skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) - assert.NoError(t, err) - assert.False(t, skip) - assert.NotContains(t, c.values, "xyz") + assert.True(t, strings.Contains(err.Error(), "property abc is used in skip_prompt_if but has no value assigned") || + strings.Contains(err.Error(), "property def is used in skip_prompt_if but has no value assigned")) // Values do not match skip condition. Prompt should not be skipped. c.values["abc"] = "foo" From 694af228cb33bb116bb774163e186a789cc8d6a1 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 12:26:55 +0530 Subject: [PATCH 09/19] lint --- libs/template/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/template/config_test.go b/libs/template/config_test.go index e0c49d026e..604176b95d 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -358,7 +358,7 @@ func TestPromptIsSkipped(t *testing.T) { // No values assigned to config. Expect error because required values are // missing - skip, err = c.skipPrompt(jsonschema.Property{ + _, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) From 8ea3f679a33d3815dbfeefab325723741f93c2bd Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 14:38:32 +0530 Subject: [PATCH 10/19] add empty anyOf check --- libs/jsonschema/instance.go | 7 +++++++ libs/jsonschema/instance_test.go | 13 +++++++++++++ libs/jsonschema/schema.go | 1 + .../instance-validate/test-empty-anyOf.json | 3 +++ 4 files changed, 24 insertions(+) create mode 100644 libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index cc33c42a6c..25d1ffc582 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -154,6 +154,13 @@ func (s *Schema) validateAnyOf(instance map[string]any) error { if s.AnyOf == nil { return nil } + + // According to the JSON schema RFC, anyOf must contain at least one schema. + // https://json-schema.org/draft/2020-12/json-schema-core + if len(s.AnyOf) == 0 { + return fmt.Errorf("anyOf must contain at least one schema") + } + // Currently, we only validate const for anyOf schemas since anyOf is // only used by skip_prompt_if, which only supports const. for _, anyOf := range s.AnyOf { diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index 14e555646b..90cea9e9de 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -264,6 +264,19 @@ func TestValidateInstanceForConst(t *testing.T) { assert.EqualError(t, schema.ValidateInstance(invalidInstanceValue), "expected value of property bar to be def. Found: xyz") } +func TestValidateInstanceForEmptySchema(t *testing.T) { + schema, err := Load("./testdata/instance-validate/test-empty-anyOf.json") + require.NoError(t, err) + + // Valid values for both foo and bar + validInstance := map[string]any{ + "foo": "abc", + "bar": "abc", + } + assert.ErrorContains(t, schema.validateAnyOf(validInstance), "anyOf must contain at least one schema") + assert.NoError(t, schema.ValidateInstance(validInstance), "anyOf must contain at least one schema") +} + func TestValidateInstanceForAnyOf(t *testing.T) { schema, err := Load("./testdata/instance-validate/test-schema-anyof.json") require.NoError(t, err) diff --git a/libs/jsonschema/schema.go b/libs/jsonschema/schema.go index 967e2e9cdc..eb8f68f0fc 100644 --- a/libs/jsonschema/schema.go +++ b/libs/jsonschema/schema.go @@ -241,6 +241,7 @@ func (schema *Schema) validate() error { schema.validateSchemaPattern, schema.validateSchemaMinimumCliVersion("v" + build.GetInfo().Version), schema.validateSchemaSkippedPropertiesHaveDefaults, + } { err := fn() if err != nil { diff --git a/libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json b/libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json new file mode 100644 index 0000000000..1172846a77 --- /dev/null +++ b/libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json @@ -0,0 +1,3 @@ +{ + "anyOf": [] +} From 89db29a0f77d853dd6e94af3b52ffad7ae11f15e Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 14:50:00 +0530 Subject: [PATCH 11/19] recursively validate for anyOf --- libs/jsonschema/instance.go | 2 +- libs/jsonschema/instance_test.go | 9 +++++---- .../testdata/instance-validate/test-schema-anyof.json | 11 +++++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 25d1ffc582..3a8a445804 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -164,7 +164,7 @@ func (s *Schema) validateAnyOf(instance map[string]any) error { // Currently, we only validate const for anyOf schemas since anyOf is // only used by skip_prompt_if, which only supports const. for _, anyOf := range s.AnyOf { - err := anyOf.validateConst(instance) + err := anyOf.ValidateInstance(instance) if err == nil { return nil } diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index 90cea9e9de..baea3d8c11 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -297,12 +297,13 @@ func TestValidateInstanceForAnyOf(t *testing.T) { assert.NoError(t, schema.validateAnyOf(validInstance)) assert.NoError(t, schema.ValidateInstance(validInstance)) - // Empty instance + // Empty instance. Invalid because "foo" is required. emptyInstanceValue := map[string]any{} - assert.NoError(t, schema.validateAnyOf(emptyInstanceValue)) - assert.NoError(t, schema.ValidateInstance(emptyInstanceValue)) + assert.ErrorContains(t, schema.validateAnyOf(emptyInstanceValue), "instance does not match any of the schemas in anyOf") + assert.ErrorContains(t, schema.ValidateInstance(emptyInstanceValue), "instance does not match any of the schemas in anyOf") - // Missing values for bar, invalid value for foo + // Missing values for bar, invalid value for foo. Passes because only "foo" + // is required in second condition. missingInstanceValue := map[string]any{ "foo": "xyz", } diff --git a/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json b/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json index 0f58b9ada7..1e38c9a96d 100644 --- a/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json +++ b/libs/jsonschema/testdata/instance-validate/test-schema-anyof.json @@ -10,7 +10,11 @@ "type": "string", "const": "abc" } - } + }, + "required": [ + "foo", + "bar" + ] }, { "properties": { @@ -18,7 +22,10 @@ "type": "string", "const": "def" } - } + }, + "required": [ + "foo" + ] } ] } From e52ac4a38684684fdc674ef72016a6406a163c12 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 14:52:34 +0530 Subject: [PATCH 12/19] remove implict required condition --- libs/jsonschema/instance.go | 10 +--------- libs/template/config.go | 21 --------------------- 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 3a8a445804..9e19344ab2 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -73,20 +73,12 @@ func (s *Schema) validateAdditionalProperties(instance map[string]any) error { return nil } -type RequiredPropertyMissingError struct { - Name string -} - -func (err RequiredPropertyMissingError) Error() string { - return fmt.Sprintf("no value provided for required property %s", err.Name) -} - // This function validates that all require properties in the schema have values // in the instance. func (s *Schema) validateRequired(instance map[string]any) error { for _, name := range s.Required { if _, ok := instance[name]; !ok { - return RequiredPropertyMissingError{Name: name} + return fmt.Errorf("no value provided for required property %s", name) } } return nil diff --git a/libs/template/config.go b/libs/template/config.go index fd07023285..5dd038e014 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -125,29 +125,8 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { return false, nil } - // All fields referred to in a SkipPromptIf condition are implicitly made required and - // we diverge from strictly following the JSON schema because it makes the author UX better. - required := make(map[string]struct{}) - for _, k := range p.Schema.SkipPromptIf.Required { - required[k] = struct{}{} - } - for k := range p.Schema.SkipPromptIf.Properties { - required[k] = struct{}{} - } - for _, schema := range p.Schema.SkipPromptIf.AnyOf { - for k := range schema.Properties { - required[k] = struct{}{} - } - } - p.Schema.SkipPromptIf.Required = maps.Keys(required) - // Validate the partial config against skip_prompt_if schema validationErr := p.Schema.SkipPromptIf.ValidateInstance(c.values) - - target := jsonschema.RequiredPropertyMissingError{} - if errors.As(validationErr, &target) { - return false, fmt.Errorf("property %s is used in skip_prompt_if but has no value assigned", target.Name) - } if validationErr != nil { return false, nil } From 2e1ee600201b2fbb59367e70d74d08e6c5fa773c Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 15:14:52 +0530 Subject: [PATCH 13/19] fix tests --- libs/jsonschema/instance.go | 2 -- libs/template/config_test.go | 12 +++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 9e19344ab2..332e0e2748 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -153,8 +153,6 @@ func (s *Schema) validateAnyOf(instance map[string]any) error { return fmt.Errorf("anyOf must contain at least one schema") } - // Currently, we only validate const for anyOf schemas since anyOf is - // only used by skip_prompt_if, which only supports const. for _, anyOf := range s.AnyOf { err := anyOf.ValidateInstance(instance) if err == nil { diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 604176b95d..baa3a3bef4 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -3,7 +3,6 @@ package template import ( "context" "fmt" - "strings" "testing" "text/template" @@ -341,6 +340,7 @@ func TestPromptIsSkipped(t *testing.T) { Const: 123, }, }, + Required: []string{"abc", "def"}, }, }, }, @@ -362,8 +362,8 @@ func TestPromptIsSkipped(t *testing.T) { Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) - assert.True(t, strings.Contains(err.Error(), "property abc is used in skip_prompt_if but has no value assigned") || - strings.Contains(err.Error(), "property def is used in skip_prompt_if but has no value assigned")) + assert.NoError(t, err) + assert.False(t, skip) // Values do not match skip condition. Prompt should not be skipped. c.values["abc"] = "foo" @@ -427,6 +427,7 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { Const: 123, }, }, + Required: []string{"abc", "def"}, }, { Properties: map[string]*jsonschema.Schema{ @@ -472,7 +473,9 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) - assert.ErrorContains(t, err, "property def is used in skip_prompt_if but has no value assigned") + assert.NoError(t, err) + assert.False(t, skip) + assert.NotContains(t, c.values, "xyz") // Values match skip condition. Prompt should be skipped. Default value should // be assigned to "xyz". @@ -492,7 +495,6 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { // be assigned to "xyz". c.values = map[string]any{ "abc": "barfoo", - "def": 0, } skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", From 1aa9092c71ab255f1388ffb4c1d5534422be60b7 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 15:25:06 +0530 Subject: [PATCH 14/19] add test for missing value still skipping prompt --- libs/jsonschema/schema.go | 1 - libs/template/config_test.go | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libs/jsonschema/schema.go b/libs/jsonschema/schema.go index eb8f68f0fc..967e2e9cdc 100644 --- a/libs/jsonschema/schema.go +++ b/libs/jsonschema/schema.go @@ -241,7 +241,6 @@ func (schema *Schema) validate() error { schema.validateSchemaPattern, schema.validateSchemaMinimumCliVersion("v" + build.GetInfo().Version), schema.validateSchemaSkippedPropertiesHaveDefaults, - } { err := fn() if err != nil { diff --git a/libs/template/config_test.go b/libs/template/config_test.go index baa3a3bef4..9343c4c3f2 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -356,14 +356,14 @@ func TestPromptIsSkipped(t *testing.T) { assert.NoError(t, err) assert.False(t, skip) - // No values assigned to config. Expect error because required values are - // missing - _, err = c.skipPrompt(jsonschema.Property{ + // No values assigned to config. Prompt should not be skipped. + skip, err = c.skipPrompt(jsonschema.Property{ Name: "xyz", Schema: c.schema.Properties["xyz"], }, testRenderer()) assert.NoError(t, err) assert.False(t, skip) + assert.NotContains(t, c.values, "xyz") // Values do not match skip condition. Prompt should not be skipped. c.values["abc"] = "foo" @@ -465,7 +465,7 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.False(t, skip) assert.NotContains(t, c.values, "xyz") - // Missing values. Prompt should not be skipped. + // def is missing value. Prompt should not be skipped. c.values = map[string]any{ "abc": "foobar", } @@ -477,6 +477,19 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.False(t, skip) assert.NotContains(t, c.values, "xyz") + // abc is missing value. Prompt should be skipped because def is optional + // in second condition. + c.values = map[string]any{ + "def": 123, + } + skip, err = c.skipPrompt(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + }, testRenderer()) + assert.NoError(t, err) + assert.True(t, skip) + assert.Equal(t, "hello-world", c.values["xyz"]) + // Values match skip condition. Prompt should be skipped. Default value should // be assigned to "xyz". c.values = map[string]any{ From 9ee0e3a72cde4aab01074b64e3c05b418edee830 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 15:29:16 +0530 Subject: [PATCH 15/19] - --- libs/jsonschema/instance.go | 1 - libs/template/config_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index 332e0e2748..4440a2fe28 100644 --- a/libs/jsonschema/instance.go +++ b/libs/jsonschema/instance.go @@ -140,7 +140,6 @@ func (s *Schema) validateConst(instance map[string]any) error { } // Validates that the instance matches at least one of the schemas in anyOf -// but will also succeed if the property values are omitted. // For more information, see https://json-schema.org/understanding-json-schema/reference/combining#anyof. func (s *Schema) validateAnyOf(instance map[string]any) error { if s.AnyOf == nil { diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 9343c4c3f2..847c2615bb 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -477,7 +477,7 @@ func TestPromptIsSkippedAnyOf(t *testing.T) { assert.False(t, skip) assert.NotContains(t, c.values, "xyz") - // abc is missing value. Prompt should be skipped because def is optional + // abc is missing value. Prompt should be skipped because abc is optional // in second condition. c.values = map[string]any{ "def": 123, From daf785d3ef6b89abaef0909fd2e918adb7ea2194 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 24 Jan 2024 15:30:31 +0530 Subject: [PATCH 16/19] - --- libs/jsonschema/instance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index baea3d8c11..47bc51a67e 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -274,7 +274,7 @@ func TestValidateInstanceForEmptySchema(t *testing.T) { "bar": "abc", } assert.ErrorContains(t, schema.validateAnyOf(validInstance), "anyOf must contain at least one schema") - assert.NoError(t, schema.ValidateInstance(validInstance), "anyOf must contain at least one schema") + assert.ErrorContains(t, schema.ValidateInstance(validInstance), "anyOf must contain at least one schema") } func TestValidateInstanceForAnyOf(t *testing.T) { From 1a3b8ad07ba42261a010b432813259859a779fc0 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 25 Jan 2024 15:15:11 +0530 Subject: [PATCH 17/19] - --- .../{test-empty-anyOf.json => test-schema-empty-anyof.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libs/jsonschema/testdata/instance-validate/{test-empty-anyOf.json => test-schema-empty-anyof.json} (100%) diff --git a/libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json b/libs/jsonschema/testdata/instance-validate/test-schema-empty-anyof.json similarity index 100% rename from libs/jsonschema/testdata/instance-validate/test-empty-anyOf.json rename to libs/jsonschema/testdata/instance-validate/test-schema-empty-anyof.json From b79a70ba19d80a7cb5e14f1716119af1699cddd4 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 25 Jan 2024 15:15:35 +0530 Subject: [PATCH 18/19] fix casing --- .../{test-schema-empty-anyof.json => test-empty-anyof.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libs/jsonschema/testdata/instance-validate/{test-schema-empty-anyof.json => test-empty-anyof.json} (100%) diff --git a/libs/jsonschema/testdata/instance-validate/test-schema-empty-anyof.json b/libs/jsonschema/testdata/instance-validate/test-empty-anyof.json similarity index 100% rename from libs/jsonschema/testdata/instance-validate/test-schema-empty-anyof.json rename to libs/jsonschema/testdata/instance-validate/test-empty-anyof.json From 2ed579a32330e6d5ae84b8fc563ea7338c4d0b71 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 25 Jan 2024 15:18:38 +0530 Subject: [PATCH 19/19] - --- libs/jsonschema/instance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/jsonschema/instance_test.go b/libs/jsonschema/instance_test.go index 47bc51a67e..2ee0be0fc6 100644 --- a/libs/jsonschema/instance_test.go +++ b/libs/jsonschema/instance_test.go @@ -265,7 +265,7 @@ func TestValidateInstanceForConst(t *testing.T) { } func TestValidateInstanceForEmptySchema(t *testing.T) { - schema, err := Load("./testdata/instance-validate/test-empty-anyOf.json") + schema, err := Load("./testdata/instance-validate/test-empty-anyof.json") require.NoError(t, err) // Valid values for both foo and bar