From 5b5a15b962f336d89ab90ad9f23d39b60b9505b1 Mon Sep 17 00:00:00 2001 From: magodo Date: Sun, 24 Nov 2024 11:03:17 +1100 Subject: [PATCH 1/3] Support dynamicvalidator --- dynamicvalidator/all.go | 57 ++++++++++++++++ dynamicvalidator/all_example_test.go | 31 +++++++++ dynamicvalidator/also_requires.go | 26 +++++++ .../also_requires_example_test.go | 31 +++++++++ dynamicvalidator/any.go | 65 ++++++++++++++++++ dynamicvalidator/any_example_test.go | 29 ++++++++ dynamicvalidator/any_with_all_warnings.go | 67 +++++++++++++++++++ .../any_with_all_warnings_example_test.go | 29 ++++++++ dynamicvalidator/at_least_one_of.go | 27 ++++++++ .../at_least_one_of_example_test.go | 31 +++++++++ dynamicvalidator/conflicts_with.go | 27 ++++++++ .../conflicts_with_example_test.go | 31 +++++++++ dynamicvalidator/doc.go | 5 ++ dynamicvalidator/exactly_one_of.go | 28 ++++++++ .../exactly_one_of_example_test.go | 31 +++++++++ internal/schemavalidator/also_requires.go | 15 +++++ internal/schemavalidator/at_least_one_of.go | 15 +++++ internal/schemavalidator/conflicts_with.go | 15 +++++ internal/schemavalidator/exactly_one_of.go | 15 +++++ 19 files changed, 575 insertions(+) create mode 100644 dynamicvalidator/all.go create mode 100644 dynamicvalidator/all_example_test.go create mode 100644 dynamicvalidator/also_requires.go create mode 100644 dynamicvalidator/also_requires_example_test.go create mode 100644 dynamicvalidator/any.go create mode 100644 dynamicvalidator/any_example_test.go create mode 100644 dynamicvalidator/any_with_all_warnings.go create mode 100644 dynamicvalidator/any_with_all_warnings_example_test.go create mode 100644 dynamicvalidator/at_least_one_of.go create mode 100644 dynamicvalidator/at_least_one_of_example_test.go create mode 100644 dynamicvalidator/conflicts_with.go create mode 100644 dynamicvalidator/conflicts_with_example_test.go create mode 100644 dynamicvalidator/doc.go create mode 100644 dynamicvalidator/exactly_one_of.go create mode 100644 dynamicvalidator/exactly_one_of_example_test.go diff --git a/dynamicvalidator/all.go b/dynamicvalidator/all.go new file mode 100644 index 00000000..7f29e2ae --- /dev/null +++ b/dynamicvalidator/all.go @@ -0,0 +1,57 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// All returns a validator which ensures that any configured attribute value +// attribute value validates against all the given validators. +// +// Use of All is only necessary when used in conjunction with Any or AnyWithAllWarnings +// as the Validators field automatically applies a logical AND. +func All(validators ...validator.Dynamic) validator.Dynamic { + return allValidator{ + validators: validators, + } +} + +var _ validator.Dynamic = allValidator{} + +// allValidator implements the validator. +type allValidator struct { + validators []validator.Dynamic +} + +// Description describes the validation in plain text formatting. +func (v allValidator) Description(ctx context.Context) string { + var descriptions []string + + for _, subValidator := range v.validators { + descriptions = append(descriptions, subValidator.Description(ctx)) + } + + return fmt.Sprintf("Value must satisfy all of the validations: %s", strings.Join(descriptions, " + ")) +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (v allValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +// ValidateDynamic performs the validation. +func (v allValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + for _, subValidator := range v.validators { + validateResp := &validator.DynamicResponse{} + + subValidator.ValidateDynamic(ctx, req, validateResp) + + resp.Diagnostics.Append(validateResp.Diagnostics...) + } +} diff --git a/dynamicvalidator/all_example_test.go b/dynamicvalidator/all_example_test.go new file mode 100644 index 00000000..9fe8f07e --- /dev/null +++ b/dynamicvalidator/all_example_test.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleAll() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Required: true, + Validators: []validator.Dynamic{ + // Validate this Dynamic value must either be: + // - "one" + dynamicvalidator.Any( + dynamicvalidator.OneOf("one"), + dynamicvalidator.All( + dynamicvalidator.NoneOf("three"), + ), + ), + }, + }, + }, + } +} diff --git a/dynamicvalidator/also_requires.go b/dynamicvalidator/also_requires.go new file mode 100644 index 00000000..15113d78 --- /dev/null +++ b/dynamicvalidator/also_requires.go @@ -0,0 +1,26 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/internal/schemavalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// AlsoRequires checks that a set of path.Expression has a non-null value, +// if the current attribute or block also has a non-null value. +// +// This implements the validation logic declaratively within the schema. +// Refer to [datasourcevalidator.RequiredTogether], +// [providervalidator.RequiredTogether], or [resourcevalidator.RequiredTogether] +// for declaring this type of validation outside the schema definition. +// +// Relative path.Expression will be resolved using the attribute or block +// being validated. +func AlsoRequires(expressions ...path.Expression) validator.Dynamic { + return schemavalidator.AlsoRequiresValidator{ + PathExpressions: expressions, + } +} diff --git a/dynamicvalidator/also_requires_example_test.go b/dynamicvalidator/also_requires_example_test.go new file mode 100644 index 00000000..4d4bb261 --- /dev/null +++ b/dynamicvalidator/also_requires_example_test.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleAlsoRequires() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Optional: true, + Validators: []validator.Dynamic{ + // Validate this attribute must be configured with other_attr. + dynamicvalidator.AlsoRequires(path.Expressions{ + path.MatchRoot("other_attr"), + }...), + }, + }, + "other_attr": schema.DynamicAttribute{ + Optional: true, + }, + }, + } +} diff --git a/dynamicvalidator/any.go b/dynamicvalidator/any.go new file mode 100644 index 00000000..42086fcf --- /dev/null +++ b/dynamicvalidator/any.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// Any returns a validator which ensures that any configured attribute value +// passes at least one of the given validators. +// +// To prevent practitioner confusion should non-passing validators have +// conflicting logic, only warnings from the passing validator are returned. +// Use AnyWithAllWarnings() to return warnings from non-passing validators +// as well. +func Any(validators ...validator.Dynamic) validator.Dynamic { + return anyValidator{ + validators: validators, + } +} + +var _ validator.Dynamic = anyValidator{} + +// anyValidator implements the validator. +type anyValidator struct { + validators []validator.Dynamic +} + +// Description describes the validation in plain text formatting. +func (v anyValidator) Description(ctx context.Context) string { + var descriptions []string + + for _, subValidator := range v.validators { + descriptions = append(descriptions, subValidator.Description(ctx)) + } + + return fmt.Sprintf("Value must satisfy at least one of the validations: %s", strings.Join(descriptions, " + ")) +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (v anyValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +// ValidateDynamic performs the validation. +func (v anyValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + for _, subValidator := range v.validators { + validateResp := &validator.DynamicResponse{} + + subValidator.ValidateDynamic(ctx, req, validateResp) + + if !validateResp.Diagnostics.HasError() { + resp.Diagnostics = validateResp.Diagnostics + + return + } + + resp.Diagnostics.Append(validateResp.Diagnostics...) + } +} diff --git a/dynamicvalidator/any_example_test.go b/dynamicvalidator/any_example_test.go new file mode 100644 index 00000000..69bf3e44 --- /dev/null +++ b/dynamicvalidator/any_example_test.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleAny() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Required: true, + Validators: []validator.Dynamic{ + // Validate this Dynamic value must either be: + // - "one" + // - Length at least 4 characters + dynamicvalidator.Any( + dynamicvalidator.OneOf("one"), + ), + }, + }, + }, + } +} diff --git a/dynamicvalidator/any_with_all_warnings.go b/dynamicvalidator/any_with_all_warnings.go new file mode 100644 index 00000000..c1614798 --- /dev/null +++ b/dynamicvalidator/any_with_all_warnings.go @@ -0,0 +1,67 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// AnyWithAllWarnings returns a validator which ensures that any configured +// attribute value passes at least one of the given validators. This validator +// returns all warnings, including failed validators. +// +// Use Any() to return warnings only from the passing validator. +func AnyWithAllWarnings(validators ...validator.Dynamic) validator.Dynamic { + return anyWithAllWarningsValidator{ + validators: validators, + } +} + +var _ validator.Dynamic = anyWithAllWarningsValidator{} + +// anyWithAllWarningsValidator implements the validator. +type anyWithAllWarningsValidator struct { + validators []validator.Dynamic +} + +// Description describes the validation in plain text formatting. +func (v anyWithAllWarningsValidator) Description(ctx context.Context) string { + var descriptions []string + + for _, subValidator := range v.validators { + descriptions = append(descriptions, subValidator.Description(ctx)) + } + + return fmt.Sprintf("Value must satisfy at least one of the validations: %s", strings.Join(descriptions, " + ")) +} + +// MarkdownDescription describes the validation in Markdown formatting. +func (v anyWithAllWarningsValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +// ValidateDynamic performs the validation. +func (v anyWithAllWarningsValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + anyValid := false + + for _, subValidator := range v.validators { + validateResp := &validator.DynamicResponse{} + + subValidator.ValidateDynamic(ctx, req, validateResp) + + if !validateResp.Diagnostics.HasError() { + anyValid = true + } + + resp.Diagnostics.Append(validateResp.Diagnostics...) + } + + if anyValid { + resp.Diagnostics = resp.Diagnostics.Warnings() + } +} diff --git a/dynamicvalidator/any_with_all_warnings_example_test.go b/dynamicvalidator/any_with_all_warnings_example_test.go new file mode 100644 index 00000000..bb8f85ab --- /dev/null +++ b/dynamicvalidator/any_with_all_warnings_example_test.go @@ -0,0 +1,29 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleAnyWithAllWarnings() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Required: true, + Validators: []validator.Dynamic{ + // Validate this Dynamic value must either be: + // - "one" + // - Length at least 4 characters + dynamicvalidator.AnyWithAllWarnings( + dynamicvalidator.OneOf("one"), + ), + }, + }, + }, + } +} diff --git a/dynamicvalidator/at_least_one_of.go b/dynamicvalidator/at_least_one_of.go new file mode 100644 index 00000000..7a833fd4 --- /dev/null +++ b/dynamicvalidator/at_least_one_of.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/internal/schemavalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// AtLeastOneOf checks that of a set of path.Expression, +// including the attribute this validator is applied to, +// at least one has a non-null value. +// +// This implements the validation logic declaratively within the tfsdk.Schema. +// Refer to [datasourcevalidator.AtLeastOneOf], +// [providervalidator.AtLeastOneOf], or [resourcevalidator.AtLeastOneOf] +// for declaring this type of validation outside the schema definition. +// +// Any relative path.Expression will be resolved using the attribute being +// validated. +func AtLeastOneOf(expressions ...path.Expression) validator.Dynamic { + return schemavalidator.AtLeastOneOfValidator{ + PathExpressions: expressions, + } +} diff --git a/dynamicvalidator/at_least_one_of_example_test.go b/dynamicvalidator/at_least_one_of_example_test.go new file mode 100644 index 00000000..6e64bbc4 --- /dev/null +++ b/dynamicvalidator/at_least_one_of_example_test.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleAtLeastOneOf() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Optional: true, + Validators: []validator.Dynamic{ + // Validate at least this attribute or other_attr should be configured. + dynamicvalidator.AtLeastOneOf(path.Expressions{ + path.MatchRoot("other_attr"), + }...), + }, + }, + "other_attr": schema.DynamicAttribute{ + Optional: true, + }, + }, + } +} diff --git a/dynamicvalidator/conflicts_with.go b/dynamicvalidator/conflicts_with.go new file mode 100644 index 00000000..1bda81c2 --- /dev/null +++ b/dynamicvalidator/conflicts_with.go @@ -0,0 +1,27 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/internal/schemavalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// ConflictsWith checks that a set of path.Expression, +// including the attribute the validator is applied to, +// do not have a value simultaneously. +// +// This implements the validation logic declaratively within the schema. +// Refer to [datasourcevalidator.Conflicting], +// [providervalidator.Conflicting], or [resourcevalidator.Conflicting] +// for declaring this type of validation outside the schema definition. +// +// Relative path.Expression will be resolved using the attribute being +// validated. +func ConflictsWith(expressions ...path.Expression) validator.Dynamic { + return schemavalidator.ConflictsWithValidator{ + PathExpressions: expressions, + } +} diff --git a/dynamicvalidator/conflicts_with_example_test.go b/dynamicvalidator/conflicts_with_example_test.go new file mode 100644 index 00000000..268f765a --- /dev/null +++ b/dynamicvalidator/conflicts_with_example_test.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleConflictsWith() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Optional: true, + Validators: []validator.Dynamic{ + // Validate this attribute must not be configured with other_attr. + dynamicvalidator.ConflictsWith(path.Expressions{ + path.MatchRoot("other_attr"), + }...), + }, + }, + "other_attr": schema.DynamicAttribute{ + Optional: true, + }, + }, + } +} diff --git a/dynamicvalidator/doc.go b/dynamicvalidator/doc.go new file mode 100644 index 00000000..de534e26 --- /dev/null +++ b/dynamicvalidator/doc.go @@ -0,0 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Package dynamicvalidator provides validators for types.Dynamic attributes and function parameters. +package dynamicvalidator diff --git a/dynamicvalidator/exactly_one_of.go b/dynamicvalidator/exactly_one_of.go new file mode 100644 index 00000000..a501c7e8 --- /dev/null +++ b/dynamicvalidator/exactly_one_of.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/internal/schemavalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +// ExactlyOneOf checks that of a set of path.Expression, +// including the attribute the validator is applied to, +// one and only one attribute has a value. +// It will also cause a validation error if none are specified. +// +// This implements the validation logic declaratively within the schema. +// Refer to [datasourcevalidator.ExactlyOneOf], +// [providervalidator.ExactlyOneOf], or [resourcevalidator.ExactlyOneOf] +// for declaring this type of validation outside the schema definition. +// +// Relative path.Expression will be resolved using the attribute being +// validated. +func ExactlyOneOf(expressions ...path.Expression) validator.Dynamic { + return schemavalidator.ExactlyOneOfValidator{ + PathExpressions: expressions, + } +} diff --git a/dynamicvalidator/exactly_one_of_example_test.go b/dynamicvalidator/exactly_one_of_example_test.go new file mode 100644 index 00000000..47be7cc8 --- /dev/null +++ b/dynamicvalidator/exactly_one_of_example_test.go @@ -0,0 +1,31 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dynamicvalidator_test + +import ( + "github.com/hashicorp/terraform-plugin-framework-validators/dynamicvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" +) + +func ExampleExactlyOneOf() { + // Used within a Schema method of a DataSource, Provider, or Resource + _ = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "example_attr": schema.DynamicAttribute{ + Optional: true, + Validators: []validator.Dynamic{ + // Validate only this attribute or other_attr is configured. + dynamicvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("other_attr"), + }...), + }, + }, + "other_attr": schema.DynamicAttribute{ + Optional: true, + }, + }, + } +} diff --git a/internal/schemavalidator/also_requires.go b/internal/schemavalidator/also_requires.go index 7abc8ae9..2f4662a7 100644 --- a/internal/schemavalidator/also_requires.go +++ b/internal/schemavalidator/also_requires.go @@ -29,6 +29,7 @@ var ( _ validator.Object = AlsoRequiresValidator{} _ validator.Set = AlsoRequiresValidator{} _ validator.String = AlsoRequiresValidator{} + _ validator.Dynamic = AlsoRequiresValidator{} ) // AlsoRequiresValidator is the underlying struct implementing AlsoRequires. @@ -257,3 +258,17 @@ func (av AlsoRequiresValidator) ValidateString(ctx context.Context, req validato resp.Diagnostics.Append(validateResp.Diagnostics...) } + +func (av AlsoRequiresValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + validateReq := AlsoRequiresValidatorRequest{ + Config: req.Config, + ConfigValue: req.ConfigValue, + Path: req.Path, + PathExpression: req.PathExpression, + } + validateResp := &AlsoRequiresValidatorResponse{} + + av.Validate(ctx, validateReq, validateResp) + + resp.Diagnostics.Append(validateResp.Diagnostics...) +} diff --git a/internal/schemavalidator/at_least_one_of.go b/internal/schemavalidator/at_least_one_of.go index a31e4507..fa932ec2 100644 --- a/internal/schemavalidator/at_least_one_of.go +++ b/internal/schemavalidator/at_least_one_of.go @@ -29,6 +29,7 @@ var ( _ validator.Object = AtLeastOneOfValidator{} _ validator.Set = AtLeastOneOfValidator{} _ validator.String = AtLeastOneOfValidator{} + _ validator.Dynamic = AtLeastOneOfValidator{} ) // AtLeastOneOfValidator is the underlying struct implementing AtLeastOneOf. @@ -257,3 +258,17 @@ func (av AtLeastOneOfValidator) ValidateString(ctx context.Context, req validato resp.Diagnostics.Append(validateResp.Diagnostics...) } + +func (av AtLeastOneOfValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + validateReq := AtLeastOneOfValidatorRequest{ + Config: req.Config, + ConfigValue: req.ConfigValue, + Path: req.Path, + PathExpression: req.PathExpression, + } + validateResp := &AtLeastOneOfValidatorResponse{} + + av.Validate(ctx, validateReq, validateResp) + + resp.Diagnostics.Append(validateResp.Diagnostics...) +} diff --git a/internal/schemavalidator/conflicts_with.go b/internal/schemavalidator/conflicts_with.go index 185d58e9..fd183893 100644 --- a/internal/schemavalidator/conflicts_with.go +++ b/internal/schemavalidator/conflicts_with.go @@ -29,6 +29,7 @@ var ( _ validator.Object = ConflictsWithValidator{} _ validator.Set = ConflictsWithValidator{} _ validator.String = ConflictsWithValidator{} + _ validator.Dynamic = ConflictsWithValidator{} ) // ConflictsWithValidator is the underlying struct implementing ConflictsWith. @@ -257,3 +258,17 @@ func (av ConflictsWithValidator) ValidateString(ctx context.Context, req validat resp.Diagnostics.Append(validateResp.Diagnostics...) } + +func (av ConflictsWithValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + validateReq := ConflictsWithValidatorRequest{ + Config: req.Config, + ConfigValue: req.ConfigValue, + Path: req.Path, + PathExpression: req.PathExpression, + } + validateResp := &ConflictsWithValidatorResponse{} + + av.Validate(ctx, validateReq, validateResp) + + resp.Diagnostics.Append(validateResp.Diagnostics...) +} diff --git a/internal/schemavalidator/exactly_one_of.go b/internal/schemavalidator/exactly_one_of.go index 40af4383..b4714a71 100644 --- a/internal/schemavalidator/exactly_one_of.go +++ b/internal/schemavalidator/exactly_one_of.go @@ -29,6 +29,7 @@ var ( _ validator.Object = ExactlyOneOfValidator{} _ validator.Set = ExactlyOneOfValidator{} _ validator.String = ExactlyOneOfValidator{} + _ validator.Dynamic = ExactlyOneOfValidator{} ) // ExactlyOneOfValidator is the underlying struct implementing ExactlyOneOf. @@ -277,3 +278,17 @@ func (av ExactlyOneOfValidator) ValidateString(ctx context.Context, req validato resp.Diagnostics.Append(validateResp.Diagnostics...) } + +func (av ExactlyOneOfValidator) ValidateDynamic(ctx context.Context, req validator.DynamicRequest, resp *validator.DynamicResponse) { + validateReq := ExactlyOneOfValidatorRequest{ + Config: req.Config, + ConfigValue: req.ConfigValue, + Path: req.Path, + PathExpression: req.PathExpression, + } + validateResp := &ExactlyOneOfValidatorResponse{} + + av.Validate(ctx, validateReq, validateResp) + + resp.Diagnostics.Append(validateResp.Diagnostics...) +} From 6c335a1766029c3a4b754f24227f2c3b154495d3 Mon Sep 17 00:00:00 2001 From: magodo Date: Sat, 7 Dec 2024 10:47:49 +1100 Subject: [PATCH 2/3] Pass tests --- dynamicvalidator/all_example_test.go | 6 ++---- dynamicvalidator/any_example_test.go | 2 +- dynamicvalidator/any_with_all_warnings_example_test.go | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dynamicvalidator/all_example_test.go b/dynamicvalidator/all_example_test.go index 9fe8f07e..65d58a29 100644 --- a/dynamicvalidator/all_example_test.go +++ b/dynamicvalidator/all_example_test.go @@ -19,10 +19,8 @@ func ExampleAll() { // Validate this Dynamic value must either be: // - "one" dynamicvalidator.Any( - dynamicvalidator.OneOf("one"), - dynamicvalidator.All( - dynamicvalidator.NoneOf("three"), - ), + dynamicvalidator.Any( /* ... */ ), + dynamicvalidator.All( /* ... */ ), ), }, }, diff --git a/dynamicvalidator/any_example_test.go b/dynamicvalidator/any_example_test.go index 69bf3e44..e65f5ef1 100644 --- a/dynamicvalidator/any_example_test.go +++ b/dynamicvalidator/any_example_test.go @@ -20,7 +20,7 @@ func ExampleAny() { // - "one" // - Length at least 4 characters dynamicvalidator.Any( - dynamicvalidator.OneOf("one"), + dynamicvalidator.Any( /* ... */ ), ), }, }, diff --git a/dynamicvalidator/any_with_all_warnings_example_test.go b/dynamicvalidator/any_with_all_warnings_example_test.go index bb8f85ab..120580b6 100644 --- a/dynamicvalidator/any_with_all_warnings_example_test.go +++ b/dynamicvalidator/any_with_all_warnings_example_test.go @@ -20,7 +20,7 @@ func ExampleAnyWithAllWarnings() { // - "one" // - Length at least 4 characters dynamicvalidator.AnyWithAllWarnings( - dynamicvalidator.OneOf("one"), + dynamicvalidator.AnyWithAllWarnings( /* ... */ ), ), }, }, From db3c7c5f0dfd72f77e941e3585bfd65e89615d5c Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Thu, 12 Dec 2024 14:43:53 -0500 Subject: [PATCH 3/3] docs updates + changelog --- .changes/unreleased/FEATURES-20241212-144210.yaml | 5 +++++ dynamicvalidator/all_example_test.go | 2 -- dynamicvalidator/any_example_test.go | 3 --- dynamicvalidator/any_with_all_warnings_example_test.go | 3 --- 4 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 .changes/unreleased/FEATURES-20241212-144210.yaml diff --git a/.changes/unreleased/FEATURES-20241212-144210.yaml b/.changes/unreleased/FEATURES-20241212-144210.yaml new file mode 100644 index 00000000..ff1a9adf --- /dev/null +++ b/.changes/unreleased/FEATURES-20241212-144210.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'dynamicvalidator: New package which contains `types.Dynamic` specific validators' +time: 2024-12-12T14:42:10.189818-05:00 +custom: + Issue: "249" diff --git a/dynamicvalidator/all_example_test.go b/dynamicvalidator/all_example_test.go index 65d58a29..e1de7cfb 100644 --- a/dynamicvalidator/all_example_test.go +++ b/dynamicvalidator/all_example_test.go @@ -16,8 +16,6 @@ func ExampleAll() { "example_attr": schema.DynamicAttribute{ Required: true, Validators: []validator.Dynamic{ - // Validate this Dynamic value must either be: - // - "one" dynamicvalidator.Any( dynamicvalidator.Any( /* ... */ ), dynamicvalidator.All( /* ... */ ), diff --git a/dynamicvalidator/any_example_test.go b/dynamicvalidator/any_example_test.go index e65f5ef1..b7cce6d6 100644 --- a/dynamicvalidator/any_example_test.go +++ b/dynamicvalidator/any_example_test.go @@ -16,9 +16,6 @@ func ExampleAny() { "example_attr": schema.DynamicAttribute{ Required: true, Validators: []validator.Dynamic{ - // Validate this Dynamic value must either be: - // - "one" - // - Length at least 4 characters dynamicvalidator.Any( dynamicvalidator.Any( /* ... */ ), ), diff --git a/dynamicvalidator/any_with_all_warnings_example_test.go b/dynamicvalidator/any_with_all_warnings_example_test.go index 120580b6..8e7ebb85 100644 --- a/dynamicvalidator/any_with_all_warnings_example_test.go +++ b/dynamicvalidator/any_with_all_warnings_example_test.go @@ -16,9 +16,6 @@ func ExampleAnyWithAllWarnings() { "example_attr": schema.DynamicAttribute{ Required: true, Validators: []validator.Dynamic{ - // Validate this Dynamic value must either be: - // - "one" - // - Length at least 4 characters dynamicvalidator.AnyWithAllWarnings( dynamicvalidator.AnyWithAllWarnings( /* ... */ ), ),