From 4f1d1e9322e5abc321d9e68a05f904863164c6ba Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Sep 2024 16:39:44 -0400 Subject: [PATCH 1/3] boolvalidator: add Equals validator This validator can be used in cases where non-null boolean value should be exactly `true` or exactly `false`. ```console % go test -count=1 ./boolvalidator/... ok github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator 0.251s ``` --- boolvalidator/equals.go | 51 ++++++++++++++++++++++++++ boolvalidator/equals_test.go | 69 ++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 boolvalidator/equals.go create mode 100644 boolvalidator/equals_test.go diff --git a/boolvalidator/equals.go b/boolvalidator/equals.go new file mode 100644 index 00000000..3170dff6 --- /dev/null +++ b/boolvalidator/equals.go @@ -0,0 +1,51 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolvalidator + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +var _ validator.Bool = equalsValidator{} + +type equalsValidator struct { + value types.Bool +} + +func (v equalsValidator) Description(ctx context.Context) string { + return fmt.Sprintf("Value must be %q", v.value) +} + +func (v equalsValidator) MarkdownDescription(ctx context.Context) string { + return v.Description(ctx) +} + +func (v equalsValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { + if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() { + return + } + + configValue := req.ConfigValue + + if !configValue.Equal(v.value) { + resp.Diagnostics.Append(validatordiag.InvalidAttributeValueMatchDiagnostic( + req.Path, + v.Description(ctx), + configValue.String(), + )) + } +} + +// Equals checks that the Bool held in the attribute matches the +// given `value` +func Equals(value bool) validator.Bool { + return equalsValidator{ + value: types.BoolValue(value), + } +} diff --git a/boolvalidator/equals_test.go b/boolvalidator/equals_test.go new file mode 100644 index 00000000..8d87af96 --- /dev/null +++ b/boolvalidator/equals_test.go @@ -0,0 +1,69 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package boolvalidator_test + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestEqualsValidator(t *testing.T) { + t.Parallel() + + type testCase struct { + in types.Bool + validator validator.Bool + expErrors int + } + + testCases := map[string]testCase{ + "simple-match": { + in: types.BoolValue(true), + validator: boolvalidator.Equals(true), + expErrors: 0, + }, + "simple-mismatch": { + in: types.BoolValue(false), + validator: boolvalidator.Equals(true), + expErrors: 1, + }, + "skip-validation-on-null": { + in: types.BoolNull(), + validator: boolvalidator.Equals(true), + expErrors: 0, + }, + "skip-validation-on-unknown": { + in: types.BoolUnknown(), + validator: boolvalidator.Equals(true), + expErrors: 0, + }, + } + + for name, test := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + req := validator.BoolRequest{ + ConfigValue: test.in, + } + res := validator.BoolResponse{} + test.validator.ValidateBool(context.TODO(), req, &res) + + if test.expErrors > 0 && !res.Diagnostics.HasError() { + t.Fatalf("expected %d error(s), got none", test.expErrors) + } + + if test.expErrors > 0 && test.expErrors != res.Diagnostics.ErrorsCount() { + t.Fatalf("expected %d error(s), got %d: %v", test.expErrors, res.Diagnostics.ErrorsCount(), res.Diagnostics) + } + + if test.expErrors == 0 && res.Diagnostics.HasError() { + t.Fatalf("expected no error(s), got %d: %v", res.Diagnostics.ErrorsCount(), res.Diagnostics) + } + }) + } +} From 2b5dcb9accc31d60a1fbae2fb8dd10fcc9d67023 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 20 Sep 2024 16:44:33 -0400 Subject: [PATCH 2/3] Update boolvalidator/equals.go Co-authored-by: Austin Valle --- boolvalidator/equals.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boolvalidator/equals.go b/boolvalidator/equals.go index 3170dff6..b63da2a4 100644 --- a/boolvalidator/equals.go +++ b/boolvalidator/equals.go @@ -42,8 +42,8 @@ func (v equalsValidator) ValidateBool(ctx context.Context, req validator.BoolReq } } -// Equals checks that the Bool held in the attribute matches the -// given `value` +// Equals returns an AttributeValidator which ensures that the configured boolean attribute +// matches the given `value`. Null (unconfigured) and unknown (known after apply) values are skipped. func Equals(value bool) validator.Bool { return equalsValidator{ value: types.BoolValue(value), From 4636145bd016f0731154d22a4319c663e7fcbba2 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 20 Sep 2024 16:49:19 -0400 Subject: [PATCH 3/3] chore: changelog --- .changes/unreleased/FEATURES-20240920-164852.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/unreleased/FEATURES-20240920-164852.yaml diff --git a/.changes/unreleased/FEATURES-20240920-164852.yaml b/.changes/unreleased/FEATURES-20240920-164852.yaml new file mode 100644 index 00000000..0a90941d --- /dev/null +++ b/.changes/unreleased/FEATURES-20240920-164852.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'boolvalidator: Added `Equals` validator' +time: 2024-09-20T16:48:52.562758-04:00 +custom: + Issue: "232"