-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding equalToSumOf int64 validator (#20)
- Loading branch information
1 parent
5816bae
commit 5937dfd
Showing
2 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package int64validator | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework-validators/validatordiag" | ||
) | ||
|
||
var _ tfsdk.AttributeValidator = equalToSumOfValidator{} | ||
|
||
// equalToSumOfValidator validates that an integer Attribute's value equals the sum of one | ||
// or more integer Attributes. | ||
type equalToSumOfValidator struct { | ||
attributesToSumPaths []*tftypes.AttributePath | ||
} | ||
|
||
// Description describes the validation in plain text formatting. | ||
func (validator equalToSumOfValidator) Description(_ context.Context) string { | ||
var attributePaths []string | ||
for _, path := range validator.attributesToSumPaths { | ||
attributePaths = append(attributePaths, path.String()) | ||
} | ||
|
||
return fmt.Sprintf("value must be equal to the sum of %s", strings.Join(attributePaths, " + ")) | ||
} | ||
|
||
// MarkdownDescription describes the validation in Markdown formatting. | ||
func (validator equalToSumOfValidator) MarkdownDescription(ctx context.Context) string { | ||
return validator.Description(ctx) | ||
} | ||
|
||
// Validate performs the validation. | ||
func (validator equalToSumOfValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) { | ||
i, ok := validateInt(ctx, request, response) | ||
|
||
if !ok { | ||
return | ||
} | ||
|
||
var sumOfAttribs int64 | ||
|
||
for _, path := range validator.attributesToSumPaths { | ||
var attribToSum types.Int64 | ||
|
||
response.Diagnostics.Append(request.Config.GetAttribute(ctx, path, &attribToSum)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
sumOfAttribs += attribToSum.Value | ||
} | ||
|
||
if i != sumOfAttribs { | ||
|
||
response.Diagnostics.Append(validatordiag.AttributeValueDiagnostic( | ||
request.AttributePath, | ||
validator.Description(ctx), | ||
fmt.Sprintf("%d", i), | ||
)) | ||
|
||
return | ||
} | ||
} | ||
|
||
// EqualToSumOf returns an AttributeValidator which ensures that any configured | ||
// attribute value: | ||
// | ||
// - Is a number, which can be represented by a 64-bit integer. | ||
// - Is exclusively equal to the sum of the given attributes. | ||
// | ||
// Null (unconfigured) and unknown (known after apply) values are skipped. | ||
func EqualToSumOf(attributesToSum []*tftypes.AttributePath) tfsdk.AttributeValidator { | ||
return equalToSumOfValidator{ | ||
attributesToSumPaths: attributesToSum, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package int64validator | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/tfsdk" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
) | ||
|
||
func TestEqualToSumOfValidator(t *testing.T) { | ||
t.Parallel() | ||
|
||
type testCase struct { | ||
val attr.Value | ||
attributesToSumPaths []*tftypes.AttributePath | ||
requestConfigRaw map[string]attr.Value | ||
expectError bool | ||
} | ||
tests := map[string]testCase{ | ||
"not an Int64": { | ||
val: types.Bool{Value: true}, | ||
expectError: true, | ||
}, | ||
"unknown Int64": { | ||
val: types.Int64{Unknown: true}, | ||
}, | ||
"null Int64": { | ||
val: types.Int64{Null: true}, | ||
}, | ||
"valid integer as Int64 more than sum of attributes": { | ||
val: types.Int64{Value: 11}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Value: 5}, | ||
"two": types.Int64{Value: 5}, | ||
}, | ||
expectError: true, | ||
}, | ||
"valid integer as Int64 less than sum of attributes": { | ||
val: types.Int64{Value: 9}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Value: 5}, | ||
"two": types.Int64{Value: 5}, | ||
}, | ||
expectError: true, | ||
}, | ||
"valid integer as Int64 equal to sum of attributes": { | ||
val: types.Int64{Value: 10}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Value: 5}, | ||
"two": types.Int64{Value: 5}, | ||
}, | ||
}, | ||
"valid integer as Int64 equal to sum of attributes, when one summed attribute is null": { | ||
val: types.Int64{Value: 8}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Null: true}, | ||
"two": types.Int64{Value: 8}, | ||
}, | ||
}, | ||
"valid integer as Int64 does not return error when all attributes are null": { | ||
val: types.Int64{Null: true}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Null: true}, | ||
"two": types.Int64{Null: true}, | ||
}, | ||
}, | ||
"valid integer as Int64 equal to sum of attributes, when one summed attribute is unknown": { | ||
val: types.Int64{Value: 8}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Unknown: true}, | ||
"two": types.Int64{Value: 8}, | ||
}, | ||
}, | ||
"valid integer as Int64 does not return error when all attributes are unknown": { | ||
val: types.Int64{Unknown: true}, | ||
attributesToSumPaths: []*tftypes.AttributePath{ | ||
tftypes.NewAttributePath().WithAttributeName("one"), | ||
tftypes.NewAttributePath().WithAttributeName("two"), | ||
}, | ||
requestConfigRaw: map[string]attr.Value{ | ||
"one": types.Int64{Unknown: true}, | ||
"two": types.Int64{Unknown: true}, | ||
}, | ||
}, | ||
} | ||
|
||
for name, test := range tests { | ||
name, test := name, test | ||
t.Run(name, func(t *testing.T) { | ||
reqConf := make(map[string]tftypes.Value, len(test.requestConfigRaw)) | ||
|
||
for k, v := range test.requestConfigRaw { | ||
val, err := v.ToTerraformValue(context.Background()) | ||
if err != nil { | ||
t.Fatalf("could not attr.Value at key:%s to tftypes.Value", k) | ||
} | ||
|
||
reqConf[k] = val | ||
} | ||
|
||
request := tfsdk.ValidateAttributeRequest{ | ||
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"), | ||
AttributeConfig: test.val, | ||
Config: tfsdk.Config{ | ||
Raw: tftypes.NewValue(tftypes.Object{}, reqConf), | ||
Schema: tfsdk.Schema{ | ||
Attributes: map[string]tfsdk.Attribute{ | ||
"test": {Type: types.Int64Type}, | ||
"one": {Type: types.Int64Type}, | ||
"two": {Type: types.Int64Type}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
response := tfsdk.ValidateAttributeResponse{} | ||
|
||
EqualToSumOf(test.attributesToSumPaths).Validate(context.Background(), request, &response) | ||
|
||
if !response.Diagnostics.HasError() && test.expectError { | ||
t.Fatal("expected error, got no error") | ||
} | ||
|
||
if response.Diagnostics.HasError() && !test.expectError { | ||
t.Fatalf("got unexpected error: %s", response.Diagnostics) | ||
} | ||
}) | ||
} | ||
} |