-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reference: #70
- Loading branch information
Showing
55 changed files
with
9,647 additions
and
27 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,84 @@ | ||
package fwschemadata | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/logging" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
) | ||
|
||
// ValueSemanticEqualityRequest represents a request for the provider to | ||
// perform semantic equality logic on a value. | ||
type ValueSemanticEqualityRequest struct { | ||
// Path is the schema-based path of the value. | ||
Path path.Path | ||
|
||
// PriorValue is the prior value. | ||
PriorValue attr.Value | ||
|
||
// ProposedNewValue is the proposed new value. NewValue in the response | ||
// contains the results of semantic equality logic. | ||
ProposedNewValue attr.Value | ||
} | ||
|
||
// ValueSemanticEqualityResponse represents a response to a | ||
// ValueSemanticEqualityRequest. | ||
type ValueSemanticEqualityResponse struct { | ||
// NewValue contains the new value based on the semantic equality logic. | ||
NewValue attr.Value | ||
|
||
// Diagnostics contains any errors and warnings for the logic. | ||
Diagnostics diag.Diagnostics | ||
} | ||
|
||
// ValueSemanticEquality runs all semantic equality logic for a value, including | ||
// recursive checking against collection and structural types. | ||
func ValueSemanticEquality(ctx context.Context, req ValueSemanticEqualityRequest, resp *ValueSemanticEqualityResponse) { | ||
ctx = logging.FrameworkWithAttributePath(ctx, req.Path.String()) | ||
|
||
// Ensure the response NewValue always starts with the proposed new value. | ||
// This is purely defensive coding to prevent subtle data handling bugs. | ||
resp.NewValue = req.ProposedNewValue | ||
|
||
// If the prior value is null or unknown, no need to check semantic equality | ||
// as the proposed new value is always correct. There is also no need to | ||
// descend further into any nesting. | ||
if req.PriorValue.IsNull() || req.PriorValue.IsUnknown() { | ||
return | ||
} | ||
|
||
// If the proposed new value is null or unknown, no need to check semantic | ||
// equality as it should never be changed back to the prior value. There is | ||
// also no need to descend further into any nesting. | ||
if req.ProposedNewValue.IsNull() || req.ProposedNewValue.IsUnknown() { | ||
return | ||
} | ||
|
||
switch req.ProposedNewValue.(type) { | ||
case basetypes.BoolValuable: | ||
ValueSemanticEqualityBool(ctx, req, resp) | ||
case basetypes.Float64Valuable: | ||
ValueSemanticEqualityFloat64(ctx, req, resp) | ||
case basetypes.Int64Valuable: | ||
ValueSemanticEqualityInt64(ctx, req, resp) | ||
case basetypes.ListValuable: | ||
ValueSemanticEqualityList(ctx, req, resp) | ||
case basetypes.MapValuable: | ||
ValueSemanticEqualityMap(ctx, req, resp) | ||
case basetypes.NumberValuable: | ||
ValueSemanticEqualityNumber(ctx, req, resp) | ||
case basetypes.ObjectValuable: | ||
ValueSemanticEqualityObject(ctx, req, resp) | ||
case basetypes.SetValuable: | ||
ValueSemanticEqualitySet(ctx, req, resp) | ||
case basetypes.StringValuable: | ||
ValueSemanticEqualityString(ctx, req, resp) | ||
} | ||
|
||
if resp.NewValue.Equal(req.PriorValue) { | ||
logging.FrameworkDebug(ctx, "Value switched to prior value due to semantic equality logic") | ||
} | ||
} |
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,51 @@ | ||
package fwschemadata | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/internal/logging" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
) | ||
|
||
// ValueSemanticEqualityBool performs bool type semantic equality. | ||
func ValueSemanticEqualityBool(ctx context.Context, req ValueSemanticEqualityRequest, resp *ValueSemanticEqualityResponse) { | ||
priorValuable, ok := req.PriorValue.(basetypes.BoolValuableWithSemanticEquals) | ||
|
||
// No changes required if the interface is not implemented. | ||
if !ok { | ||
return | ||
} | ||
|
||
proposedNewValuable, ok := req.ProposedNewValue.(basetypes.BoolValuableWithSemanticEquals) | ||
|
||
// No changes required if the interface is not implemented. | ||
if !ok { | ||
return | ||
} | ||
|
||
logging.FrameworkTrace( | ||
ctx, | ||
"Calling provider defined type-based SemanticEquals", | ||
map[string]interface{}{ | ||
logging.KeyValueType: proposedNewValuable.String(), | ||
}, | ||
) | ||
|
||
usePriorValue, diags := proposedNewValuable.BoolSemanticEquals(ctx, priorValuable) | ||
|
||
logging.FrameworkTrace( | ||
ctx, | ||
"Called provider defined type-based SemanticEquals", | ||
map[string]interface{}{ | ||
logging.KeyValueType: proposedNewValuable.String(), | ||
}, | ||
) | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if !usePriorValue { | ||
return | ||
} | ||
|
||
resp.NewValue = priorValuable | ||
} |
124 changes: 124 additions & 0 deletions
124
internal/fwschemadata/value_semantic_equality_bool_test.go
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,124 @@ | ||
package fwschemadata_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" | ||
testtypes "github.com/hashicorp/terraform-plugin-framework/internal/testing/types" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
) | ||
|
||
func TestValueSemanticEqualityBool(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
request fwschemadata.ValueSemanticEqualityRequest | ||
expected *fwschemadata.ValueSemanticEqualityResponse | ||
}{ | ||
"BoolValue": { | ||
request: fwschemadata.ValueSemanticEqualityRequest{ | ||
Path: path.Root("test"), | ||
PriorValue: types.BoolValue(false), | ||
ProposedNewValue: types.BoolValue(true), | ||
}, | ||
expected: &fwschemadata.ValueSemanticEqualityResponse{ | ||
NewValue: types.BoolValue(true), | ||
}, | ||
}, | ||
"BoolValuableWithSemanticEquals-true": { | ||
request: fwschemadata.ValueSemanticEqualityRequest{ | ||
Path: path.Root("test"), | ||
PriorValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(false), | ||
SemanticEquals: true, | ||
}, | ||
ProposedNewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(true), | ||
SemanticEquals: true, | ||
}, | ||
}, | ||
expected: &fwschemadata.ValueSemanticEqualityResponse{ | ||
NewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(false), | ||
SemanticEquals: true, | ||
}, | ||
}, | ||
}, | ||
"BoolValuableWithSemanticEquals-false": { | ||
request: fwschemadata.ValueSemanticEqualityRequest{ | ||
Path: path.Root("test"), | ||
PriorValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(false), | ||
SemanticEquals: false, | ||
}, | ||
ProposedNewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(true), | ||
SemanticEquals: false, | ||
}, | ||
}, | ||
expected: &fwschemadata.ValueSemanticEqualityResponse{ | ||
NewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(true), | ||
SemanticEquals: false, | ||
}, | ||
}, | ||
}, | ||
"BoolValuableWithSemanticEquals-diagnostics": { | ||
request: fwschemadata.ValueSemanticEqualityRequest{ | ||
Path: path.Root("test"), | ||
PriorValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(false), | ||
SemanticEquals: false, | ||
SemanticEqualsDiagnostics: diag.Diagnostics{ | ||
diag.NewErrorDiagnostic("test summary 1", "test detail 1"), | ||
diag.NewErrorDiagnostic("test summary 2", "test detail 2"), | ||
}, | ||
}, | ||
ProposedNewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(true), | ||
SemanticEquals: false, | ||
SemanticEqualsDiagnostics: diag.Diagnostics{ | ||
diag.NewErrorDiagnostic("test summary 1", "test detail 1"), | ||
diag.NewErrorDiagnostic("test summary 2", "test detail 2"), | ||
}, | ||
}, | ||
}, | ||
expected: &fwschemadata.ValueSemanticEqualityResponse{ | ||
NewValue: testtypes.BoolValueWithSemanticEquals{ | ||
BoolValue: types.BoolValue(true), | ||
SemanticEquals: false, | ||
SemanticEqualsDiagnostics: diag.Diagnostics{ | ||
diag.NewErrorDiagnostic("test summary 1", "test detail 1"), | ||
diag.NewErrorDiagnostic("test summary 2", "test detail 2"), | ||
}, | ||
}, | ||
Diagnostics: diag.Diagnostics{ | ||
diag.NewErrorDiagnostic("test summary 1", "test detail 1"), | ||
diag.NewErrorDiagnostic("test summary 2", "test detail 2"), | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for name, testCase := range testCases { | ||
name, testCase := name, testCase | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
got := &fwschemadata.ValueSemanticEqualityResponse{ | ||
NewValue: testCase.request.ProposedNewValue, | ||
} | ||
|
||
fwschemadata.ValueSemanticEqualityBool(context.Background(), testCase.request, got) | ||
|
||
if diff := cmp.Diff(got, testCase.expected); diff != "" { | ||
t.Errorf("unexpected difference: %s", diff) | ||
} | ||
}) | ||
} | ||
} |
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,51 @@ | ||
package fwschemadata | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hashicorp/terraform-plugin-framework/internal/logging" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
) | ||
|
||
// ValueSemanticEqualityFloat64 performs float64 type semantic equality. | ||
func ValueSemanticEqualityFloat64(ctx context.Context, req ValueSemanticEqualityRequest, resp *ValueSemanticEqualityResponse) { | ||
priorValuable, ok := req.PriorValue.(basetypes.Float64ValuableWithSemanticEquals) | ||
|
||
// No changes required if the interface is not implemented. | ||
if !ok { | ||
return | ||
} | ||
|
||
proposedNewValuable, ok := req.ProposedNewValue.(basetypes.Float64ValuableWithSemanticEquals) | ||
|
||
// No changes required if the interface is not implemented. | ||
if !ok { | ||
return | ||
} | ||
|
||
logging.FrameworkTrace( | ||
ctx, | ||
"Calling provider defined type-based SemanticEquals", | ||
map[string]interface{}{ | ||
logging.KeyValueType: proposedNewValuable.String(), | ||
}, | ||
) | ||
|
||
usePriorValue, diags := proposedNewValuable.Float64SemanticEquals(ctx, priorValuable) | ||
|
||
logging.FrameworkTrace( | ||
ctx, | ||
"Called provider defined type-based SemanticEquals", | ||
map[string]interface{}{ | ||
logging.KeyValueType: proposedNewValuable.String(), | ||
}, | ||
) | ||
|
||
resp.Diagnostics.Append(diags...) | ||
|
||
if !usePriorValue { | ||
return | ||
} | ||
|
||
resp.NewValue = priorValuable | ||
} |
Oops, something went wrong.