diff --git a/libs/jsonschema/instance.go b/libs/jsonschema/instance.go index da7453ec5..5ae554e7b 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 9a2c80484..14e555646 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 a02c80b50..29791aa90 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"