Skip to content

Commit

Permalink
tfsdk: Prevent configuration handling error when Schema contains Bloc…
Browse files Browse the repository at this point in the history
…ks (#371)

Reference: #328

Previously the framework could return errors similar to the following:

```
Error: Error parsing config

The provider had a problem parsing the config. Report this to the provider
developer:
AttributeName("output").ElementKeyInt(0): error decoding object; expected 0
attributes, got 5
```

New unit testing failures before fix:

```
--- FAIL: TestBlockAttributeType (0.00s)
    --- FAIL: TestBlockAttributeType/NestingMode-Set (0.00s)
        /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:103: unexpected difference:   types.SetType(
            - 	s`types.SetType[types.ObjectType["test_attribute":types.StringType]]`,
            + 	s`types.SetType[types.ObjectType["test_attribute":types.StringType, "test_block":types.SetType[types.ObjectType["test_block_attribute":types.StringType]]]]`,
              )
    --- FAIL: TestBlockAttributeType/NestingMode-List (0.00s)
        /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:103: unexpected difference:   types.ListType(
            - 	s`types.ListType[types.ObjectType["test_attribute":types.StringType]]`,
            + 	s`types.ListType[types.ObjectType["test_attribute":types.StringType, "test_block":types.ListType[types.ObjectType["test_block_attribute":types.StringType]]]]`,
              )

--- FAIL: TestBlockTerraformType (0.00s)
    --- FAIL: TestBlockTerraformType/NestingMode-List (0.00s)
        /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:199: unexpected difference:   tftypes.List(
            - 	s`tftypes.List[tftypes.Object["test_attribute":tftypes.String]]`,
            + 	s`tftypes.List[tftypes.Object["test_attribute":tftypes.String, "test_block":tftypes.List[tftypes.Object["test_block_attribute":tftypes.String]]]]`,
              )
    --- FAIL: TestBlockTerraformType/NestingMode-Set (0.00s)
        /Users/bflad/src/github.com/hashicorp/terraform-plugin-framework/tfsdk/block_test.go:199: unexpected difference:   tftypes.Set(
            - 	s`tftypes.Set[tftypes.Object["test_attribute":tftypes.String]]`,
            + 	s`tftypes.Set[tftypes.Object["test_attribute":tftypes.String, "test_block":tftypes.Set[tftypes.Object["test_block_attribute":tftypes.String]]]]`,
              )
```
  • Loading branch information
bflad authored Jun 13, 2022
1 parent c76b9a4 commit dfb09a6
Show file tree
Hide file tree
Showing 4 changed files with 572 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/371.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
tfsdk: Prevented configuration handling error when `Schema` contained `Blocks`
```
365 changes: 365 additions & 0 deletions tfsdk/attribute_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,365 @@
package tfsdk

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

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

testCases := map[string]struct {
attribute Attribute
expected attr.Type
}{
"Attributes-ListNestedAttributes": {
attribute: Attribute{
Attributes: ListNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: types.ListType{
ElemType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_nested_attribute": types.StringType,
},
},
},
},
"Attributes-MapNestedAttributes": {
attribute: Attribute{
Attributes: MapNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: types.MapType{
ElemType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_nested_attribute": types.StringType,
},
},
},
},
"Attributes-SetNestedAttributes": {
attribute: Attribute{
Attributes: SetNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: types.SetType{
ElemType: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_nested_attribute": types.StringType,
},
},
},
},
"Attributes-SingleNestedAttributes": {
attribute: Attribute{
Attributes: SingleNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_nested_attribute": types.StringType,
},
},
},
"Type-BoolType": {
attribute: Attribute{
Required: true,
Type: types.BoolType,
},
expected: types.BoolType,
},
"Type-Float64Type": {
attribute: Attribute{
Required: true,
Type: types.Float64Type,
},
expected: types.Float64Type,
},
"Type-Int64Type": {
attribute: Attribute{
Required: true,
Type: types.Int64Type,
},
expected: types.Int64Type,
},
"Type-ListType": {
attribute: Attribute{
Required: true,
Type: types.ListType{
ElemType: types.StringType,
},
},
expected: types.ListType{
ElemType: types.StringType,
},
},
"Type-MapType": {
attribute: Attribute{
Required: true,
Type: types.MapType{
ElemType: types.StringType,
},
},
expected: types.MapType{
ElemType: types.StringType,
},
},
"Type-ObjectType": {
attribute: Attribute{
Required: true,
Type: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_object_attribute": types.StringType,
},
},
},
expected: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_object_attribute": types.StringType,
},
},
},
"Type-NumberType": {
attribute: Attribute{
Required: true,
Type: types.NumberType,
},
expected: types.NumberType,
},
"Type-SetType": {
attribute: Attribute{
Required: true,
Type: types.SetType{
ElemType: types.StringType,
},
},
expected: types.SetType{
ElemType: types.StringType,
},
},
"Type-StringType": {
attribute: Attribute{
Required: true,
Type: types.StringType,
},
expected: types.StringType,
},
}

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

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

got := testCase.attribute.attributeType()

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}

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

testCases := map[string]struct {
attribute Attribute
expected tftypes.Type
}{
"Attributes-ListNestedAttributes": {
attribute: Attribute{
Attributes: ListNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: tftypes.List{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_nested_attribute": tftypes.String,
},
},
},
},
"Attributes-MapNestedAttributes": {
attribute: Attribute{
Attributes: MapNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: tftypes.Map{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_nested_attribute": tftypes.String,
},
},
},
},
"Attributes-SetNestedAttributes": {
attribute: Attribute{
Attributes: SetNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: tftypes.Set{
ElementType: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_nested_attribute": tftypes.String,
},
},
},
},
"Attributes-SingleNestedAttributes": {
attribute: Attribute{
Attributes: SingleNestedAttributes(map[string]Attribute{
"test_nested_attribute": {
Required: true,
Type: types.StringType,
},
}),
Required: true,
},
expected: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_nested_attribute": tftypes.String,
},
},
},
"Type-BoolType": {
attribute: Attribute{
Required: true,
Type: types.BoolType,
},
expected: tftypes.Bool,
},
"Type-Float64Type": {
attribute: Attribute{
Required: true,
Type: types.Float64Type,
},
expected: tftypes.Number,
},
"Type-Int64Type": {
attribute: Attribute{
Required: true,
Type: types.Int64Type,
},
expected: tftypes.Number,
},
"Type-ListType": {
attribute: Attribute{
Required: true,
Type: types.ListType{
ElemType: types.StringType,
},
},
expected: tftypes.List{
ElementType: tftypes.String,
},
},
"Type-MapType": {
attribute: Attribute{
Required: true,
Type: types.MapType{
ElemType: types.StringType,
},
},
expected: tftypes.Map{
ElementType: tftypes.String,
},
},
"Type-NumberType": {
attribute: Attribute{
Required: true,
Type: types.NumberType,
},
expected: tftypes.Number,
},
"Type-ObjectType": {
attribute: Attribute{
Required: true,
Type: types.ObjectType{
AttrTypes: map[string]attr.Type{
"test_object_attribute": types.StringType,
},
},
},
expected: tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test_object_attribute": tftypes.String,
},
},
},
"Type-SetType": {
attribute: Attribute{
Required: true,
Type: types.SetType{
ElemType: types.StringType,
},
},
expected: tftypes.Set{
ElementType: tftypes.String,
},
},
"Type-StringType": {
attribute: Attribute{
Required: true,
Type: types.StringType,
},
expected: tftypes.String,
},
}

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

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

got := testCase.attribute.terraformType(context.Background())

if diff := cmp.Diff(got, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
2 changes: 1 addition & 1 deletion tfsdk/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (b Block) attributeType() attr.Type {
attrType.AttrTypes[attrName] = attr.attributeType()
}

for blockName, block := range b.Attributes {
for blockName, block := range b.Blocks {
attrType.AttrTypes[blockName] = block.attributeType()
}

Expand Down
Loading

0 comments on commit dfb09a6

Please sign in to comment.