From d042e57a65b1d854434276a134ed520a9cbf473a Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 8 Nov 2023 19:56:49 +0100 Subject: [PATCH 01/12] Add support for conditional prompting in bundle init --- libs/jsonschema/extension.go | 4 ++++ libs/jsonschema/schema.go | 33 +++++++++++++++++++++++++++++++++ libs/template/config.go | 21 +++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/libs/jsonschema/extension.go b/libs/jsonschema/extension.go index ffb77bd834..ffa1c3018c 100644 --- a/libs/jsonschema/extension.go +++ b/libs/jsonschema/extension.go @@ -26,4 +26,8 @@ type Extension struct { // If the CLI version is less than this value, then validation for this // schema will fail. MinDatabricksCliVersion string `json:"min_databricks_cli_version,omitempty"` + + // Skip prompting if this schema is satisfied by the user entered values. In + // that case the default value of the property is used instead. + SkipPromptIf *Schema `json:"skip_prompt_if,omitempty"` } diff --git a/libs/jsonschema/schema.go b/libs/jsonschema/schema.go index 83213791ec..99b98a2cd6 100644 --- a/libs/jsonschema/schema.go +++ b/libs/jsonschema/schema.go @@ -20,6 +20,10 @@ type Schema struct { // IDE. This is manually injected here using schema.Docs Description string `json:"description,omitempty"` + // Expected value for the object. Validation succeeds if the instance is equal + // to this keyword's value. + Const any `json:"const,omitempty"` + // Schemas for the fields of an struct. The keys are the first json tag. // The values are the schema for the type of the field Properties map[string]*Schema `json:"properties,omitempty"` @@ -118,6 +122,18 @@ func (schema *Schema) validateSchemaDefaultValueTypes() error { return nil } +func (schema *Schema) validateConstValueTypes() error { + for name, property := range schema.Properties { + if property.Const == nil { + continue + } + if err := validateType(property.Const, property.Type); err != nil { + return fmt.Errorf("type validation for const value of property %s failed: %w", name, err) + } + } + return nil +} + // Validate enum field values for properties are consistent with types. func (schema *Schema) validateSchemaEnumValueTypes() error { for name, property := range schema.Properties { @@ -203,14 +219,25 @@ func (schema *Schema) validateSchemaMinimumCliVersion(currentVersion string) fun } } +func (schema *Schema) validateSchemaSkippedPropertiesHaveDefaults() error { + for name, property := range schema.Properties { + if property.SkipPromptIf != nil && property.Default == nil { + return fmt.Errorf("property %q has a skip_prompt_if clause but no default value", name) + } + } + return nil +} + func (schema *Schema) validate() error { for _, fn := range []func() error{ schema.validateSchemaPropertyTypes, schema.validateSchemaDefaultValueTypes, schema.validateSchemaEnumValueTypes, + schema.validateConstValueTypes, schema.validateSchemaDefaultValueIsInEnums, schema.validateSchemaPattern, schema.validateSchemaMinimumCliVersion("v" + build.GetInfo().Version), + schema.validateSchemaSkippedPropertiesHaveDefaults, } { err := fn() if err != nil { @@ -248,6 +275,12 @@ func Load(path string) (*Schema, error) { return nil, fmt.Errorf("failed to parse default value for property %s: %w", name, err) } } + if property.Const != nil { + property.Const, err = toInteger(property.Const) + if err != nil { + return nil, fmt.Errorf("failed to parse const value for property %s: %w", name, err) + } + } for i, enum := range property.Enum { property.Enum[i], err = toInteger(enum) if err != nil { diff --git a/libs/template/config.go b/libs/template/config.go index 51283e035f..63a15c958e 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -102,6 +102,27 @@ func (c *config) assignDefaultValues(r *renderer) error { // Prompts user for values for properties that do not have a value set yet func (c *config) promptForValues(r *renderer) error { for _, p := range c.schema.OrderedProperties() { + + // Skip prompting if required. + if p.Schema.SkipPromptIf != nil { + skip := true + + // Check if conditionals for skipping are satisfied + for name, property := range p.Schema.SkipPromptIf.Properties { + if v, ok := c.values[name]; ok && v == property.Const { + continue + } + skip = false + break + } + + // Skip prompting and assign default values if conditions are satisfied + if skip { + c.values[p.Name] = p.Schema.Default + continue + } + } + name := p.Name property := p.Schema From 2454bfbd427ad0d785cd0192d2437fa564cc753d Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 8 Nov 2023 20:10:05 +0100 Subject: [PATCH 02/12] add tests for the new validation --- libs/jsonschema/schema_test.go | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/libs/jsonschema/schema_test.go b/libs/jsonschema/schema_test.go index a750f44af1..f902942817 100644 --- a/libs/jsonschema/schema_test.go +++ b/libs/jsonschema/schema_test.go @@ -250,3 +250,52 @@ func TestValidateSchemaMinimumCliVersion(t *testing.T) { err = s.validateSchemaMinimumCliVersion("v0.0.0-dev")() assert.NoError(t, err) } + +func TestValidateSchemaConstTypes(t *testing.T) { + s := &Schema{ + Properties: map[string]*Schema{ + "foo": { + Type: "string", + Const: "abc", + }, + }, + } + err := s.validate() + assert.NoError(t, err) + + s = &Schema{ + Properties: map[string]*Schema{ + "foo": { + Type: "string", + Const: 123, + }, + }, + } + err = s.validate() + assert.EqualError(t, err, "type validation for const value of property foo failed: expected type string, but value is 123") +} + +func TestValidateSchemaSkippedPropertiesHaveDefaults(t *testing.T) { + s := &Schema{ + Properties: map[string]*Schema{ + "foo": { + Type: "string", + Extension: Extension{SkipPromptIf: &Schema{}}, + }, + }, + } + err := s.validate() + assert.EqualError(t, err, "property \"foo\" has a skip_prompt_if clause but no default value") + + s = &Schema{ + Properties: map[string]*Schema{ + "foo": { + Type: "string", + Default: "abc", + Extension: Extension{SkipPromptIf: &Schema{}}, + }, + }, + } + err = s.validate() + assert.NoError(t, err) +} From 228554ade2be512eff6c7f289be8bc21eb926819 Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 8 Nov 2023 20:15:55 +0100 Subject: [PATCH 03/12] add test for the const integer conversion --- libs/jsonschema/schema_test.go | 6 ++++++ .../testdata/schema-load-int/schema-invalid-const.json | 9 +++++++++ .../testdata/schema-load-int/schema-valid.json | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 libs/jsonschema/testdata/schema-load-int/schema-invalid-const.json diff --git a/libs/jsonschema/schema_test.go b/libs/jsonschema/schema_test.go index f902942817..cf1f127672 100644 --- a/libs/jsonschema/schema_test.go +++ b/libs/jsonschema/schema_test.go @@ -48,6 +48,7 @@ func TestSchemaLoadIntegers(t *testing.T) { assert.NoError(t, err) assert.Equal(t, int64(1), schema.Properties["abc"].Default) assert.Equal(t, []any{int64(1), int64(2), int64(3)}, schema.Properties["abc"].Enum) + assert.Equal(t, int64(5), schema.Properties["def"].Const) } func TestSchemaLoadIntegersWithInvalidDefault(t *testing.T) { @@ -60,6 +61,11 @@ func TestSchemaLoadIntegersWithInvalidEnums(t *testing.T) { assert.EqualError(t, err, "failed to parse enum value 2.4 at index 1 for property abc: expected integer value, got: 2.4") } +func TestSchemaLoadIntergersWithInvalidConst(t *testing.T) { + _, err := Load("./testdata/schema-load-int/schema-invalid-const.json") + assert.EqualError(t, err, "failed to parse const value for property def: expected integer value, got: 5.1") +} + func TestSchemaValidateDefaultType(t *testing.T) { invalidSchema := &Schema{ Properties: map[string]*Schema{ diff --git a/libs/jsonschema/testdata/schema-load-int/schema-invalid-const.json b/libs/jsonschema/testdata/schema-load-int/schema-invalid-const.json new file mode 100644 index 0000000000..9c1b3c0d3a --- /dev/null +++ b/libs/jsonschema/testdata/schema-load-int/schema-invalid-const.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "def": { + "type": "integer", + "const": 5.1 + } + } +} diff --git a/libs/jsonschema/testdata/schema-load-int/schema-valid.json b/libs/jsonschema/testdata/schema-load-int/schema-valid.json index a1167a6c98..425d7c5a97 100644 --- a/libs/jsonschema/testdata/schema-load-int/schema-valid.json +++ b/libs/jsonschema/testdata/schema-load-int/schema-valid.json @@ -5,6 +5,10 @@ "type": "integer", "default": 1, "enum": [1,2,3] + }, + "def": { + "type": "integer", + "const": 5 } } } From dc1d074f54f42ff58e8b81d033d78662e66b3b7e Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 8 Nov 2023 20:18:02 +0100 Subject: [PATCH 04/12] - --- libs/template/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/template/config.go b/libs/template/config.go index 63a15c958e..79340f52bf 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -116,7 +116,7 @@ func (c *config) promptForValues(r *renderer) error { break } - // Skip prompting and assign default values if conditions are satisfied + // Skip prompting and assign default value if conditions are satisfied if skip { c.values[p.Name] = p.Schema.Default continue From b185f28ef18a1dcc1ca17f9845747a1775c83ed8 Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 8 Nov 2023 20:23:13 +0100 Subject: [PATCH 05/12] - --- libs/template/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/template/config.go b/libs/template/config.go index 79340f52bf..68cdecfb71 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -107,7 +107,7 @@ func (c *config) promptForValues(r *renderer) error { if p.Schema.SkipPromptIf != nil { skip := true - // Check if conditionals for skipping are satisfied + // Check if conditions for skipping are satisfied for name, property := range p.Schema.SkipPromptIf.Properties { if v, ok := c.values[name]; ok && v == property.Const { continue From d691aa19080b1b4086eda9264ac48adfb616cc7a Mon Sep 17 00:00:00 2001 From: monalisa Date: Thu, 9 Nov 2023 14:51:50 +0100 Subject: [PATCH 06/12] edit field comment --- libs/jsonschema/extension.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/jsonschema/extension.go b/libs/jsonschema/extension.go index ffa1c3018c..4e5481a8b4 100644 --- a/libs/jsonschema/extension.go +++ b/libs/jsonschema/extension.go @@ -27,7 +27,7 @@ type Extension struct { // schema will fail. MinDatabricksCliVersion string `json:"min_databricks_cli_version,omitempty"` - // Skip prompting if this schema is satisfied by the user entered values. In + // Skip prompting if this schema is satisfied by the configuration already present. In // that case the default value of the property is used instead. SkipPromptIf *Schema `json:"skip_prompt_if,omitempty"` } From 77bc75dbc35b12843b8fd1de77d215c7240d73b1 Mon Sep 17 00:00:00 2001 From: monalisa Date: Thu, 9 Nov 2023 15:10:03 +0100 Subject: [PATCH 07/12] clarified const comment --- libs/jsonschema/schema.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/jsonschema/schema.go b/libs/jsonschema/schema.go index 99b98a2cd6..443e7af6e6 100644 --- a/libs/jsonschema/schema.go +++ b/libs/jsonschema/schema.go @@ -20,8 +20,8 @@ type Schema struct { // IDE. This is manually injected here using schema.Docs Description string `json:"description,omitempty"` - // Expected value for the object. Validation succeeds if the instance is equal - // to this keyword's value. + // Expected value for the JSON object. The object value must be equal to this + // field if it's specified in the schema. Const any `json:"const,omitempty"` // Schemas for the fields of an struct. The keys are the first json tag. From 88ba1ddfe598fcd614fbc496d22d2a5d88ea3701 Mon Sep 17 00:00:00 2001 From: monalisa Date: Thu, 9 Nov 2023 16:22:28 +0100 Subject: [PATCH 08/12] add unit test for skipping --- libs/template/config.go | 44 ++++++++++++--------- libs/template/config_test.go | 74 ++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/libs/template/config.go b/libs/template/config.go index 68cdecfb71..d8201f7fcf 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -99,28 +99,36 @@ func (c *config) assignDefaultValues(r *renderer) error { return nil } +func (c *config) isSkipped(p jsonschema.Property) bool { + if p.Schema.SkipPromptIf == nil { + return false + } + + // Check if conditions for skipping are satisfied + skip := true + for name, property := range p.Schema.SkipPromptIf.Properties { + if v, ok := c.values[name]; ok && v == property.Const { + continue + } + skip = false + break + } + + // Skip prompting and assign default value if conditions are satisfied + if skip { + c.values[p.Name] = p.Schema.Default + } + + return skip +} + // Prompts user for values for properties that do not have a value set yet func (c *config) promptForValues(r *renderer) error { for _, p := range c.schema.OrderedProperties() { - // Skip prompting if required. - if p.Schema.SkipPromptIf != nil { - skip := true - - // Check if conditions for skipping are satisfied - for name, property := range p.Schema.SkipPromptIf.Properties { - if v, ok := c.values[name]; ok && v == property.Const { - continue - } - skip = false - break - } - - // Skip prompting and assign default value if conditions are satisfied - if skip { - c.values[p.Name] = p.Schema.Default - continue - } + // Skip prompting if we can. + if c.isSkipped(p) { + continue } name := p.Name diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 69e7054fec..70b487c4b4 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -194,3 +194,77 @@ func TestTemplateSchemaErrorsWithEmptyDescription(t *testing.T) { _, err := newConfig(context.Background(), "./testdata/config-test-schema/invalid-test-schema.json") assert.EqualError(t, err, "template property property-without-description is missing a description") } + +func TestPromptIsSkipped(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{ + Properties: map[string]*jsonschema.Schema{ + "abc": { + Const: "foobar", + }, + "def": { + Const: 123, + }, + }, + }, + }, + }, + }, + }, + } + + // No skip condition defined. Prompt should not be skipped. + assert.False(t, c.isSkipped(jsonschema.Property{ + Name: "abc", + Schema: c.schema.Properties["abc"], + })) + + // No values assigned to config. Prompt should not be skipped. + assert.False(t, c.isSkipped(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + })) + assert.NotContains(t, c.values, "xyz") + + // Values do not match skip condition. Prompt should not be skipped. + c.values["abc"] = "foo" + c.values["def"] = 123 + assert.False(t, c.isSkipped(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + })) + assert.NotContains(t, c.values, "xyz") + + // Values do not match skip condition. Prompt should not be skipped. + c.values["abc"] = "foobar" + c.values["def"] = 1234 + assert.False(t, c.isSkipped(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + })) + 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 + assert.True(t, c.isSkipped(jsonschema.Property{ + Name: "xyz", + Schema: c.schema.Properties["xyz"], + })) + assert.Equal(t, "hello-world", c.values["xyz"]) +} From 13986bf65e0d9aeaacb0c84acacf2712749ef31d Mon Sep 17 00:00:00 2001 From: monalisa Date: Tue, 14 Nov 2023 23:50:14 +0100 Subject: [PATCH 09/12] Add test case for empty schema --- libs/template/config_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 70b487c4b4..24af488f9a 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -195,6 +195,32 @@ func TestTemplateSchemaErrorsWithEmptyDescription(t *testing.T) { assert.EqualError(t, err, "template property property-without-description is missing a description") } +func TestPromptIsSkippedWhenEmpty(t *testing.T) { + c := config{ + ctx: context.Background(), + values: make(map[string]any), + schema: &jsonschema.Schema{ + Properties: map[string]*jsonschema.Schema{ + "always-skip": { + Type: "string", + Default: "some-default-value", + Extension: jsonschema.Extension{ + SkipPromptIf: &jsonschema.Schema{}, + }, + }, + }, + }, + } + + // We should always skip the prompt here. An empty JSON schema by definition + // matches all possible configurations. + assert.True(t, c.isSkipped(jsonschema.Property{ + Name: "always-skip", + Schema: c.schema.Properties["always-skip"], + })) + assert.Equal(t, "some-default-value", c.values["always-skip"]) +} + func TestPromptIsSkipped(t *testing.T) { c := config{ ctx: context.Background(), From 1fae053fcc9e58f8bb2e2047d1e2f8a9570ef936 Mon Sep 17 00:00:00 2001 From: monalisa Date: Tue, 21 Nov 2023 17:18:46 +0100 Subject: [PATCH 10/12] address comments --- libs/template/config.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/libs/template/config.go b/libs/template/config.go index d8201f7fcf..251e5a0b87 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -105,32 +105,18 @@ func (c *config) isSkipped(p jsonschema.Property) bool { } // Check if conditions for skipping are satisfied - skip := true for name, property := range p.Schema.SkipPromptIf.Properties { if v, ok := c.values[name]; ok && v == property.Const { continue } - skip = false - break - } - - // Skip prompting and assign default value if conditions are satisfied - if skip { - c.values[p.Name] = p.Schema.Default + return false } - - return skip + return true } // Prompts user for values for properties that do not have a value set yet func (c *config) promptForValues(r *renderer) error { for _, p := range c.schema.OrderedProperties() { - - // Skip prompting if we can. - if c.isSkipped(p) { - continue - } - name := p.Name property := p.Schema @@ -153,6 +139,15 @@ func (c *config) promptForValues(r *renderer) error { } } + // Skip prompting if we can. + if c.isSkipped(p) { + c.values[name], err = property.ParseString(defaultVal) + if err != nil { + return err + } + continue + } + description, err := r.executeTemplate(property.Description) if err != nil { return err From 90c6851bff17c2479558b2ce4b42a65048398e49 Mon Sep 17 00:00:00 2001 From: monalisa Date: Tue, 21 Nov 2023 18:12:45 +0100 Subject: [PATCH 11/12] expand the scope of skipPrompt function --- libs/template/config.go | 56 ++++++++++++++------- libs/template/config_test.go | 97 ++++++++++++++++++++++++++++++------ 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/libs/template/config.go b/libs/template/config.go index 251e5a0b87..c18351564e 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -99,19 +99,47 @@ func (c *config) assignDefaultValues(r *renderer) error { return nil } -func (c *config) isSkipped(p jsonschema.Property) bool { +func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { + // Config already has a value assigned. We don't have to prompt for a user input. + if _, ok := c.values[p.Name]; ok { + return true, nil + } + if p.Schema.SkipPromptIf == nil { - return false + return false, nil } - // Check if conditions for skipping are satisfied + // Check if conditions specified by template author for skipping the prompt + // are satisfied. + skip := true for name, property := range p.Schema.SkipPromptIf.Properties { if v, ok := c.values[name]; ok && v == property.Const { continue } - return false + skip = false + break + } + if !skip { + return false, nil + } + + if p.Schema.Default == nil { + return false, fmt.Errorf("property %s has skip_prompt_if set but no default value", p.Name) + } + + // Assign default value to property if we are skipping it. + if p.Schema.Type != jsonschema.StringType { + c.values[p.Name] = p.Schema.Default + return true, nil + } + + // Execute the default value as a template and assign it to the property. + var err error + c.values[p.Name], err = r.executeTemplate(p.Schema.Default.(string)) + if err != nil { + return false, err } - return true + return true, nil } // Prompts user for values for properties that do not have a value set yet @@ -120,14 +148,17 @@ func (c *config) promptForValues(r *renderer) error { name := p.Name property := p.Schema - // Config already has a value assigned - if _, ok := c.values[name]; ok { + // Skip prompting if we can. + skip, err := c.skipPrompt(p, r) + if err != nil { + return err + } + if skip { continue } // Compute default value to display by converting it to a string var defaultVal string - var err error if property.Default != nil { defaultValRaw, err := property.DefaultString() if err != nil { @@ -139,15 +170,6 @@ func (c *config) promptForValues(r *renderer) error { } } - // Skip prompting if we can. - if c.isSkipped(p) { - c.values[name], err = property.ParseString(defaultVal) - if err != nil { - return err - } - continue - } - description, err := r.executeTemplate(property.Description) if err != nil { return err diff --git a/libs/template/config_test.go b/libs/template/config_test.go index 24af488f9a..affa827769 100644 --- a/libs/template/config_test.go +++ b/libs/template/config_test.go @@ -3,6 +3,7 @@ package template import ( "context" "testing" + "text/template" "github.com/databricks/cli/cmd/root" "github.com/databricks/cli/libs/jsonschema" @@ -195,6 +196,15 @@ func TestTemplateSchemaErrorsWithEmptyDescription(t *testing.T) { assert.EqualError(t, err, "template property property-without-description is missing a description") } +func testRenderer() *renderer { + return &renderer{ + config: map[string]any{ + "fruit": "apples", + }, + baseTemplate: template.New(""), + } +} + func TestPromptIsSkippedWhenEmpty(t *testing.T) { c := config{ ctx: context.Background(), @@ -203,7 +213,7 @@ func TestPromptIsSkippedWhenEmpty(t *testing.T) { Properties: map[string]*jsonschema.Schema{ "always-skip": { Type: "string", - Default: "some-default-value", + Default: "I like {{.fruit}}", Extension: jsonschema.Extension{ SkipPromptIf: &jsonschema.Schema{}, }, @@ -214,11 +224,60 @@ func TestPromptIsSkippedWhenEmpty(t *testing.T) { // We should always skip the prompt here. An empty JSON schema by definition // matches all possible configurations. - assert.True(t, c.isSkipped(jsonschema.Property{ + skip, err := c.skipPrompt(jsonschema.Property{ Name: "always-skip", Schema: c.schema.Properties["always-skip"], - })) - assert.Equal(t, "some-default-value", c.values["always-skip"]) + }, testRenderer()) + assert.NoError(t, err) + assert.True(t, skip) + assert.Equal(t, "I like apples", c.values["always-skip"]) +} + +func TestPromptSkipErrorsWithEmptyDefault(t *testing.T) { + c := config{ + ctx: context.Background(), + values: make(map[string]any), + schema: &jsonschema.Schema{ + Properties: map[string]*jsonschema.Schema{ + "no-default": { + Type: "string", + Extension: jsonschema.Extension{ + SkipPromptIf: &jsonschema.Schema{}, + }, + }, + }, + }, + } + + _, err := c.skipPrompt(jsonschema.Property{ + Name: "no-default", + Schema: c.schema.Properties["no-default"], + }, testRenderer()) + assert.EqualError(t, err, "property no-default has skip_prompt_if set but no default value") +} + +func TestPromptIsSkippedIfValueIsAssigned(t *testing.T) { + c := config{ + ctx: context.Background(), + values: make(map[string]any), + schema: &jsonschema.Schema{ + Properties: map[string]*jsonschema.Schema{ + "already-assigned": { + Type: "string", + Default: "some-default-value", + }, + }, + }, + } + + c.values["already-assigned"] = "some-value" + skip, err := c.skipPrompt(jsonschema.Property{ + Name: "already-assigned", + Schema: c.schema.Properties["already-assigned"], + }, testRenderer()) + assert.NoError(t, err) + assert.True(t, skip) + assert.Equal(t, "some-value", c.values["already-assigned"]) } func TestPromptIsSkipped(t *testing.T) { @@ -254,43 +313,53 @@ func TestPromptIsSkipped(t *testing.T) { } // No skip condition defined. Prompt should not be skipped. - assert.False(t, c.isSkipped(jsonschema.Property{ + skip, err := c.skipPrompt(jsonschema.Property{ Name: "abc", Schema: c.schema.Properties["abc"], - })) + }, testRenderer()) + assert.NoError(t, err) + assert.False(t, skip) // No values assigned to config. Prompt should not be skipped. - assert.False(t, c.isSkipped(jsonschema.Property{ + 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" c.values["def"] = 123 - assert.False(t, c.isSkipped(jsonschema.Property{ + 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"] = "foobar" c.values["def"] = 1234 - assert.False(t, c.isSkipped(jsonschema.Property{ + 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 - assert.True(t, c.isSkipped(jsonschema.Property{ + 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 e681a918e07d74972d62b00fd8f31f6520a4b08f Mon Sep 17 00:00:00 2001 From: monalisa Date: Wed, 29 Nov 2023 18:33:25 +0100 Subject: [PATCH 12/12] remove skip variable --- libs/template/config.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libs/template/config.go b/libs/template/config.go index c18351564e..b5bf3ef098 100644 --- a/libs/template/config.go +++ b/libs/template/config.go @@ -110,16 +110,11 @@ func (c *config) skipPrompt(p jsonschema.Property, r *renderer) (bool, error) { } // Check if conditions specified by template author for skipping the prompt - // are satisfied. - skip := true + // 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 } - skip = false - break - } - if !skip { return false, nil }