Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tf5muxserver+tf6muxserver: Support GetProviderSchema response ServerCapabilities #133

Merged
merged 5 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230207-090911.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'tf5muxserver+tf6muxserver: Support Terraform 1.3+ PlanResourceChange on destroy
for underlying servers which enable the capability, such as terraform-plugin-framework'
time: 2023-02-07T09:09:11.640268-05:00
custom:
Issue: "133"
2 changes: 2 additions & 0 deletions internal/tf5dynamicvalue/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package tf5dynamicvalue contains shared *tfprotov5.DynamicValue functions.
package tf5dynamicvalue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tf5muxserver
package tf5dynamicvalue

import (
"fmt"
Expand All @@ -7,8 +7,8 @@ import (
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// dynamicValueEquals performs equality checking of DynamicValue.
func dynamicValueEquals(schemaType tftypes.Type, i *tfprotov5.DynamicValue, j *tfprotov5.DynamicValue) (bool, error) {
// Equals performs equality checking of two given *tfprotov5.DynamicValue.
func Equals(schemaType tftypes.Type, i *tfprotov5.DynamicValue, j *tfprotov5.DynamicValue) (bool, error) {
if i == nil {
return j == nil, nil
}
Expand Down
236 changes: 236 additions & 0 deletions internal/tf5dynamicvalue/equals_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
package tf5dynamicvalue_test

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tftypes"
"github.com/hashicorp/terraform-plugin-mux/internal/tf5dynamicvalue"
)

func TestEquals(t *testing.T) {
t.Parallel()

testCases := map[string]struct {
schemaType tftypes.Type
dynamicValue1 *tfprotov5.DynamicValue
dynamicValue2 *tfprotov5.DynamicValue
expected bool
expectedError error
}{
"all-missing": {
schemaType: nil,
dynamicValue1: nil,
dynamicValue2: nil,
expected: true,
},
"first-missing": {
schemaType: nil,
dynamicValue1: nil,
dynamicValue2: &tfprotov5.DynamicValue{},
expected: false,
},
"second-missing": {
schemaType: nil,
dynamicValue1: &tfprotov5.DynamicValue{},
dynamicValue2: nil,
expected: false,
},
"missing-type": {
schemaType: nil,
dynamicValue1: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
dynamicValue2: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
expected: false,
expectedError: fmt.Errorf("unable to unmarshal DynamicValue: missing Type"),
},
"mismatched-type": {
schemaType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_bool_attribute": tftypes.Bool,
},
},
dynamicValue1: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
dynamicValue2: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
expected: false,
expectedError: fmt.Errorf("unable to unmarshal DynamicValue: unknown attribute \"test_string_attribute\""),
},
"String-different-value": {
schemaType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
dynamicValue1: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value-1"),
},
),
),
dynamicValue2: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value-2"),
},
),
),
expected: false,
},
"String-equal-value": {
schemaType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
dynamicValue1: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
dynamicValue2: tf5dynamicvalue.Must(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_string_attribute": tftypes.String,
},
},
map[string]tftypes.Value{
"test_string_attribute": tftypes.NewValue(tftypes.String, "test-value"),
},
),
),
expected: true,
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

got, err := tf5dynamicvalue.Equals(testCase.schemaType, testCase.dynamicValue1, testCase.dynamicValue2)

if err != nil {
if testCase.expectedError == nil {
t.Fatalf("wanted no error, got error: %s", err)
}

if !strings.Contains(err.Error(), testCase.expectedError.Error()) {
t.Fatalf("wanted error %q, got error: %s", testCase.expectedError.Error(), err.Error())
}
}

if err == nil && testCase.expectedError != nil {
t.Fatalf("got no error, wanted err: %s", testCase.expectedError)
}

if got != testCase.expected {
t.Errorf("expected %t, got %t", testCase.expected, got)
}
})
}
}
28 changes: 28 additions & 0 deletions internal/tf5dynamicvalue/is_null.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package tf5dynamicvalue

import (
"fmt"

"github.com/hashicorp/terraform-plugin-go/tfprotov5"
)

// IsNull returns true if the given *tfprotov5.DynamicValue is nil or
// represents a null value.
func IsNull(schema *tfprotov5.Schema, dynamicValue *tfprotov5.DynamicValue) (bool, error) {
if dynamicValue == nil {
return true, nil
}

// Panic prevention
if schema == nil {
return false, fmt.Errorf("unable to unmarshal DynamicValue: missing Type")
}

tfValue, err := dynamicValue.Unmarshal(schema.ValueType())

if err != nil {
return false, fmt.Errorf("unable to unmarshal DynamicValue: %w", err)
}

return tfValue.IsNull(), nil
}
Loading