From 57d4cc024a2ac03163e499679a532b37156a5703 Mon Sep 17 00:00:00 2001 From: Stef Forrester Date: Thu, 15 Apr 2021 16:31:20 -0700 Subject: [PATCH] Add a warning/error option to schema validation Add new schema options to configure the warning/error level of validation functions. This allows the following functions to be used as a warning-level diag instead of a fatal error: "ConflictsWith", "AtLeastOneOf", "ExactlyOneOf", "RequiredWith". The ConditionsMode allows provider developers to toggle between "warning" and "error" level messages. The ConditionsMessage is an optional additional message to be displayed to users at run time. --- helper/schema/schema.go | 31 +++++++++++++++++++++-------- helper/schema/schema_test.go | 38 ++++++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/helper/schema/schema.go b/helper/schema/schema.go index 7146bef766b..32acf76aa1f 100644 --- a/helper/schema/schema.go +++ b/helper/schema/schema.go @@ -198,6 +198,14 @@ type Schema struct { AtLeastOneOf []string RequiredWith []string + // ConditionsMode specifies the behavior of the SDK when one of the + // above conditions are met. (ConflictsWith, ExactlyOneOf, etc). + // When set to WARNING, a warning message is displayed to the user. + // When set to ERROR (default), a fatal error is returned. + // Optionally, a message can be returned with the warning or error. + ConditionsMode string + ConditionsMessage string + // When Deprecated is set, this attribute is deprecated. // // A deprecated field still works, but will probably stop working in near @@ -1449,12 +1457,19 @@ func (m schemaMap) validate( ok = raw != nil } + var conditionsMode diag.Severity + if schema.ConditionsMode == strings.ToLower("warning") { + conditionsMode = diag.Warning + } else { + conditionsMode = diag.Error + } + err := validateExactlyOneAttribute(k, schema, c) if err != nil { return append(diags, diag.Diagnostic{ - Severity: diag.Error, + Severity: conditionsMode, Summary: "ExactlyOne", - Detail: err.Error(), + Detail: fmt.Sprintf("%s %s", schema.ConditionsMessage, err.Error()), AttributePath: path, }) } @@ -1462,9 +1477,9 @@ func (m schemaMap) validate( err = validateAtLeastOneAttribute(k, schema, c) if err != nil { return append(diags, diag.Diagnostic{ - Severity: diag.Error, + Severity: conditionsMode, Summary: "AtLeastOne", - Detail: err.Error(), + Detail: fmt.Sprintf("%s %s", schema.ConditionsMessage, err.Error()), AttributePath: path, }) } @@ -1494,9 +1509,9 @@ func (m schemaMap) validate( err = validateRequiredWithAttribute(k, schema, c) if err != nil { return append(diags, diag.Diagnostic{ - Severity: diag.Error, + Severity: conditionsMode, Summary: "RequiredWith", - Detail: err.Error(), + Detail: fmt.Sprintf("%s %s", schema.ConditionsMessage, err.Error()), AttributePath: path, }) } @@ -1521,9 +1536,9 @@ func (m schemaMap) validate( err = validateConflictingAttributes(k, schema, c) if err != nil { return append(diags, diag.Diagnostic{ - Severity: diag.Error, + Severity: conditionsMode, Summary: "ConflictsWith", - Detail: err.Error(), + Detail: fmt.Sprintf("%s %s", schema.ConditionsMessage, err.Error()), AttributePath: path, }) } diff --git a/helper/schema/schema_test.go b/helper/schema/schema_test.go index 389d98559aa..7e24e0f1489 100644 --- a/helper/schema/schema_test.go +++ b/helper/schema/schema_test.go @@ -5991,7 +5991,33 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, Errors: []error{ - fmt.Errorf(`Error: ConflictsWith: "blacklist": conflicts with whitelist`), + fmt.Errorf(`Error: ConflictsWith: "blacklist": conflicts with whitelist`), + }, + }, + + "Conflicting attributes generate warning": { + Schema: map[string]*Schema{ + "whitelist": { + Type: TypeString, + Optional: true, + }, + "blacklist": { + Type: TypeString, + Optional: true, + ConflictsWith: []string{"whitelist"}, + ConditionsMode: "warning", + ConditionsMessage: "This functionality will be removed in a later release.", + }, + }, + + Config: map[string]interface{}{ + "whitelist": "white-val", + "blacklist": "black-val", + }, + + Err: false, + Warnings: []string{ + `Warning: ConflictsWith: This functionality will be removed in a later release. "blacklist": conflicts with whitelist`, }, }, @@ -6087,8 +6113,8 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, Errors: []error{ - fmt.Errorf(`Error: ConflictsWith: "blacklist": conflicts with greenlist`), - fmt.Errorf(`Error: ConflictsWith: "greenlist": conflicts with blacklist`), + fmt.Errorf(`Error: ConflictsWith: "blacklist": conflicts with greenlist`), + fmt.Errorf(`Error: ConflictsWith: "greenlist": conflicts with blacklist`), }, }, @@ -6132,7 +6158,7 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, Errors: []error{ - fmt.Errorf(`Error: ConflictsWith: "optional_att": conflicts with required_att`), + fmt.Errorf(`Error: ConflictsWith: "optional_att": conflicts with required_att`), }, }, @@ -6159,8 +6185,8 @@ func TestSchemaMap_Validate(t *testing.T) { Err: true, Errors: []error{ - fmt.Errorf(`Error: ConflictsWith: "bar_att": conflicts with foo_att`), - fmt.Errorf(`Error: ConflictsWith: "foo_att": conflicts with bar_att`), + fmt.Errorf(`Error: ConflictsWith: "bar_att": conflicts with foo_att`), + fmt.Errorf(`Error: ConflictsWith: "foo_att": conflicts with bar_att`), }, },