From 5b8a4b5240364883db65d2c588ed6abf76ec2af0 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 7 Oct 2022 11:26:08 +0100 Subject: [PATCH] Adding support for using ValueWithAttrs interface (#508) --- attr/value.go | 8 + internal/fwserver/attr_value.go | 15 +- .../fwserver/attribute_plan_modification.go | 36 +- internal/fwserver/block_plan_modification.go | 38 +- .../fwserver/block_plan_modification_test.go | 2619 +++++++++++++++-- tfsdk/block_test.go | 15 +- types/object.go | 14 + 7 files changed, 2486 insertions(+), 259 deletions(-) diff --git a/attr/value.go b/attr/value.go index cbfd53691..7d06589f9 100644 --- a/attr/value.go +++ b/attr/value.go @@ -46,3 +46,11 @@ type Value interface { // compatibility guarantees within the framework. String() string } + +type ValueWithAttrs interface { + Value + + GetAttrs() map[string]Value + + SetAttrs(map[string]Value) ValueWithAttrs +} diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index bfdea12d0..3d982827a 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -36,16 +36,20 @@ func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Dia return m, nil } +// coerceObjectValue no longer coerces value to types.Object, but performs a type assertion to verify that value fills +// attr.ValueWithAttrs interface. func coerceObjectValue(schemaPath path.Path, value attr.Value) (attr.Value, diag.Diagnostics) { - object, ok := value.Type(context.Background()).(attr.TypeWithAttributeTypes) + object, ok := value.(attr.ValueWithAttrs) if !ok { - return object.ValueType(context.Background()), diag.Diagnostics{ + // TODO: Verify whether returning value rather than the equivalent of types.Object{Null: true} for value is acceptable. + // https://github.com/hashicorp/terraform-plugin-framework/blob/main/internal/fwserver/attr_value.go#L42 + return value, diag.Diagnostics{ attributePlanModificationWalkError(schemaPath, value), } } - return value, nil + return object, nil } func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { @@ -127,12 +131,11 @@ func objectAttributeValue(ctx context.Context, object attr.Value, attributeName return objectAttributeValueFromTerraformValue(ctx, object, attributeName, description, tftypes.UnknownValue) } - attrTypes := object.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() - attrType := attrTypes[attributeName] + attrValues := object.(attr.ValueWithAttrs).GetAttrs() // A panic here indicates a bug somewhere else in the framework or an // invalid test case. - return attrType.ValueType(ctx), nil + return attrValues[attributeName], nil } func objectAttributeValueFromTerraformValue(ctx context.Context, object attr.Value, attributeName string, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index b4f6981b0..37c55e2f3 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -143,7 +143,11 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() + + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -189,13 +193,13 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private } - planList.Elems[idx] = planObject + planList.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan = planList @@ -251,7 +255,11 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() + + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -297,13 +305,13 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private } - planSet.Elems[idx] = planObject + planSet.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan = planSet @@ -359,7 +367,11 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() + + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -405,13 +417,13 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private } - planMap.Elems[key] = planObject + planMap.Elems[key] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan = planMap @@ -440,7 +452,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() if len(planObjectAttrs) == 0 { return @@ -490,13 +502,13 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private } - resp.AttributePlan = planObject + resp.AttributePlan = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) default: err := fmt.Errorf("unknown attribute nesting mode (%T: %v) at path: %s", nm, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index 44b85c233..6c2da4c40 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -109,7 +109,11 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() + + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -155,7 +159,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -205,13 +209,13 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = blockResp.AttributePlan resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private } - planList.Elems[idx] = planObject + planList.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan = planList @@ -267,7 +271,11 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() + + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -313,7 +321,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -363,13 +371,13 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = blockResp.AttributePlan resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private } - planSet.Elems[idx] = planObject + planSet.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan = planSet @@ -398,11 +406,11 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - if planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() == nil { - planObject.Type(ctx).(attr.TypeWithAttributeTypes).WithAttributeTypes(make(map[string]attr.Type)) - } + planObjectAttrs := planObject.(attr.ValueWithAttrs).GetAttrs() - planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + if planObjectAttrs == nil { + planObjectAttrs = make(map[string]attr.Value) + } for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -448,7 +456,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = attrResp.AttributePlan resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -498,13 +506,13 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) + planObjectAttrs[name] = blockResp.AttributePlan resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private } - resp.AttributePlan = planObject + resp.AttributePlan = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) default: err := fmt.Errorf("unknown block plan modification nesting mode (%T: %v) at path: %s", nm, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( diff --git a/internal/fwserver/block_plan_modification_test.go b/internal/fwserver/block_plan_modification_test.go index 8bd32a22d..02ed7f42f 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -2,6 +2,7 @@ package fwserver import ( "context" + "fmt" "testing" "github.com/google/go-cmp/cmp" @@ -18,6 +19,68 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +var ( + _ attr.Type = CustomType{} +) + +type CustomType struct { + types.ObjectType +} + +func (t CustomType) Equal(candidate attr.Type) bool { + other, ok := candidate.(CustomType) + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t CustomType) ValueType(_ context.Context) attr.Value { + return CustomValue{ + types.Object{ + AttrTypes: t.AttrTypes, + }, + } +} + +var ( + _ attr.ValueWithAttrs = CustomValue{} +) + +type CustomValue struct { + types.Object +} + +func (t CustomValue) SetAttrs(attrs map[string]attr.Value) attr.ValueWithAttrs { + t.Object.Attrs = attrs + + return t +} + +func (t CustomValue) Type(_ context.Context) attr.Type { + return CustomType{ + types.ObjectType{AttrTypes: t.AttrTypes}, + } +} + +func (t CustomValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + if t.AttrTypes == nil { + return tftypes.Value{}, fmt.Errorf("cannot convert CustomValue to tftypes.Value if AttrTypes field is not set") + } + + return t.Object.ToTerraformValue(ctx) +} + +func (t CustomValue) Equal(c attr.Value) bool { + other, ok := c.(CustomValue) + if !ok { + return false + } + + return t.Object.Equal(other.Object) +} + func TestBlockModifyPlan(t *testing.T) { t.Parallel() @@ -39,6 +102,25 @@ func TestBlockModifyPlan(t *testing.T) { } } + schemaCustomType := func(blockPlanModifiers tfsdk.AttributePlanModifiers, nestedAttrPlanModifiers tfsdk.AttributePlanModifiers) tfsdk.Schema { + return tfsdk.Schema{ + Blocks: map[string]tfsdk.Block{ + "test": { + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nestedAttrPlanModifiers, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: blockPlanModifiers, + }, + }, + } + } + schemaTfValue := func(nestedAttrValue string) tftypes.Value { return tftypes.NewValue( tftypes.Object{ @@ -153,6 +235,87 @@ func TestBlockModifyPlan(t *testing.T) { } } + modifyAttributePlanRequestCustomType := func(attrPath path.Path, schema tfsdk.Schema, values modifyAttributePlanValues) tfsdk.ModifyAttributePlanRequest { + return tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: values.config}, + }, + }, + }, + }, + }, + AttributePath: attrPath, + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: values.plan}, + }, + }, + }, + }, + }, + AttributeState: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: values.state}, + }, + }, + }, + }, + }, + Config: tfsdk.Config{ + Raw: schemaTfValue(values.config), + Schema: schema, + }, + Plan: tfsdk.Plan{ + Raw: schemaTfValue(values.plan), + Schema: schema, + }, + State: tfsdk.State{ + Raw: schemaTfValue(values.state), + Schema: schema, + }, + } + } + modifyAttributePlanWithPrivateRequest := func(attrPath path.Path, schema tfsdk.Schema, values modifyAttributePlanValues, privateProviderData *privatestate.ProviderData) tfsdk.ModifyAttributePlanRequest { req := modifyAttributePlanRequest(attrPath, schema, values) req.Private = privateProviderData @@ -160,6 +323,13 @@ func TestBlockModifyPlan(t *testing.T) { return req } + modifyAttributePlanWithPrivateRequestCustomType := func(attrPath path.Path, schema tfsdk.Schema, values modifyAttributePlanValues, privateProviderData *privatestate.ProviderData) tfsdk.ModifyAttributePlanRequest { + req := modifyAttributePlanRequestCustomType(attrPath, schema, values) + req.Private = privateProviderData + + return req + } + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), }) @@ -214,6 +384,52 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, + "no-plan-modifiers-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: nil, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, nil), + modifyAttributePlanValues{ + config: "testvalue", + plan: "testvalue", + state: "testvalue", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + }, + }, "block-modified": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -251,6 +467,46 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "block-modified-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierNullListCustomType{}, + }, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + testBlockPlanModifierNullList{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "TESTATTRONE", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + }, + Private: testEmptyProviderData, + }, + }, "block-request-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -298,8 +554,9 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-response-private": { + "block-request-private-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -309,34 +566,39 @@ func TestBlockModifyPlan(t *testing.T) { }, NestingMode: tfsdk.BlockNestingModeList, PlanModifiers: []tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateSet{}, + testBlockPlanModifierPrivateGet{}, }, }, - req: modifyAttributePlanRequest( + req: modifyAttributePlanWithPrivateRequestCustomType( path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateSet{}, + schemaCustomType([]tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateGet{}, }, nil), modifyAttributePlanValues{ config: "TESTATTRONE", plan: "TESTATTRONE", state: "TESTATTRONE", }, + testProviderData, ), expectedResp: ModifyAttributePlanResponse{ AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "TESTATTRONE"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTATTRONE"}, + }, }, }, }, @@ -344,24 +606,33 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-list-null-plan": { + "block-response-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, + Type: types.StringType, + Required: true, + PlanModifiers: nil, }, }, NestingMode: tfsdk.BlockNestingModeList, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateSet{}, }, }, - req: tfsdk.ModifyAttributePlanRequest{ - AttributeConfig: types.List{ + req: modifyAttributePlanRequest( + path.Root("test"), + schema([]tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateSet{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "TESTATTRONE", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -373,51 +644,66 @@ func TestBlockModifyPlan(t *testing.T) { "nested_attr": types.StringType, }, Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + "nested_attr": types.String{Value: "TESTATTRONE"}, }, }, }, }, - AttributePath: path.Root("test"), - AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Null: true, - }, - AttributeState: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, + Private: testProviderData, + }, + }, + "block-response-private-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, }, - Elems: []attr.Value{ - types.Object{ + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateSet{}, + }, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateSet{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "TESTATTRONE", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, - }, }, }, - }, - }, - expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTATTRONE"}, + }, + }, }, }, - Null: true, }, Private: testProviderData, }, }, - "block-list-null-state": { + "block-list-null-plan": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { @@ -453,6 +739,14 @@ func TestBlockModifyPlan(t *testing.T) { }, AttributePath: path.Root("test"), AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, + }, + AttributeState: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -469,14 +763,6 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - AttributeState: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Null: true, - }, }, expectedResp: ModifyAttributePlanResponse{ AttributePlan: types.List{ @@ -485,22 +771,14 @@ func TestBlockModifyPlan(t *testing.T) { "nested_attr": types.StringType, }, }, - Elems: []attr.Value{ - types.Object{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, - }, - }, - }, + Null: true, }, Private: testProviderData, }, }, - "block-list-nested-private": { + "block-list-null-plan-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -517,53 +795,54 @@ func TestBlockModifyPlan(t *testing.T) { }, req: tfsdk.ModifyAttributePlanRequest{ AttributeConfig: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, }, }, AttributePath: path.Root("test"), AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, - }, }, }, + Null: true, }, AttributeState: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, }, @@ -571,26 +850,19 @@ func TestBlockModifyPlan(t *testing.T) { }, expectedResp: ModifyAttributePlanResponse{ AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, - }, }, }, + Null: true, }, Private: testProviderData, }, }, - "block-set-null-plan": { + "block-list-null-state": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { @@ -601,13 +873,13 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - NestingMode: tfsdk.BlockNestingModeSet, + NestingMode: tfsdk.BlockNestingModeList, PlanModifiers: tfsdk.AttributePlanModifiers{ planmodifiers.TestAttrPlanPrivateModifierSet{}, }, }, req: tfsdk.ModifyAttributePlanRequest{ - AttributeConfig: types.Set{ + AttributeConfig: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -625,15 +897,7 @@ func TestBlockModifyPlan(t *testing.T) { }, }, AttributePath: path.Root("test"), - AttributePlan: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Null: true, - }, - AttributeState: types.Set{ + AttributePlan: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -650,21 +914,39 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, + AttributeState: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, + }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.Set{ + AttributePlan: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, }, - Null: true, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, }, Private: testProviderData, }, }, - "block-set-null-state": { + "block-list-null-state-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -674,70 +956,84 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - NestingMode: tfsdk.BlockNestingModeSet, + NestingMode: tfsdk.BlockNestingModeList, PlanModifiers: tfsdk.AttributePlanModifiers{ planmodifiers.TestAttrPlanPrivateModifierSet{}, }, }, req: tfsdk.ModifyAttributePlanRequest{ - AttributeConfig: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + AttributeConfig: types.List{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, }, }, AttributePath: path.Root("test"), - AttributePlan: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, }, }, - AttributeState: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, + AttributeState: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, }, }, Null: true, }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, }, @@ -745,7 +1041,7 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-set-nested-private": { + "block-list-nested-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { @@ -756,13 +1052,13 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - NestingMode: tfsdk.BlockNestingModeSet, + NestingMode: tfsdk.BlockNestingModeList, PlanModifiers: tfsdk.AttributePlanModifiers{ planmodifiers.TestAttrPlanPrivateModifierSet{}, }, }, req: tfsdk.ModifyAttributePlanRequest{ - AttributeConfig: types.Set{ + AttributeConfig: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -780,7 +1076,7 @@ func TestBlockModifyPlan(t *testing.T) { }, }, AttributePath: path.Root("test"), - AttributePlan: types.Set{ + AttributePlan: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -797,7 +1093,7 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - AttributeState: types.Set{ + AttributeState: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -816,7 +1112,7 @@ func TestBlockModifyPlan(t *testing.T) { }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.Set{ + AttributePlan: types.List{ ElemType: types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -836,33 +1132,678 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-list-nested-block-list": { + "block-list-nested-private-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ - "id": { + "nested_attr": { Type: types.StringType, - Computed: true, - Optional: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.UseStateForUnknown(), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, }, }, }, - Blocks: map[string]tfsdk.Block{ - "list": { - Attributes: map[string]tfsdk.Attribute{ - "nested_computed": { - Type: types.StringType, - Computed: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.UseStateForUnknown(), - }, - }, - "nested_required": { - Type: types.StringType, - Required: true, - }, - }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributeState: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-set-null-plan": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, + }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, + }, + Private: testProviderData, + }, + }, + "block-set-null-plan-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + }, + AttributeState: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + }, + Private: testProviderData, + }, + }, + "block-set-null-state": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-set-null-state-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributeState: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-set-nested-private": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-set-nested-private-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + AttributeState: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-list-nested-block-list": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "id": { + Type: types.StringType, + Computed: true, + Optional: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.UseStateForUnknown(), + }, + }, + }, + Blocks: map[string]tfsdk.Block{ + "list": { + Attributes: map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }, NestingMode: tfsdk.BlockNestingModeList, }, }, @@ -1067,15 +2008,316 @@ func TestBlockModifyPlan(t *testing.T) { "nested_required": types.StringType, }, }, - Elems: []attr.Value{ - types.Object{ - AttrTypes: map[string]attr.Type{ - "nested_computed": types.StringType, - "nested_required": types.StringType, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue"}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "block-list-nested-block-list-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "id": { + Type: types.StringType, + Computed: true, + Optional: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.UseStateForUnknown(), + }, + }, + }, + Blocks: map[string]tfsdk.Block{ + "list": { + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Attrs: map[string]attr.Value{ + "id": types.String{Value: "configvalue"}, + "list": types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Null: true}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Attrs: map[string]attr.Value{ + "id": types.String{Value: "one"}, + "list": types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Unknown: true}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + AttributeState: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Attrs: map[string]attr.Value{ + "id": types.String{Value: "one"}, + "list": types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue"}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Attrs: map[string]attr.Value{ + "id": types.String{Value: "one"}, + "list": types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, }, - Attrs: map[string]attr.Value{ - "nested_computed": types.String{Value: "statevalue"}, - "nested_required": types.String{Value: "configvalue"}, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue"}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, }, }, }, @@ -1231,6 +2473,173 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "block-set-nested-usestateforunknown-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeSet, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Null: true}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + }, + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Null: true}, + "nested_required": types.String{Value: "testvalue2"}, + }, + }, + }}, + }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Unknown: true}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + }, + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Unknown: true}, + "nested_required": types.String{Value: "testvalue2"}, + }, + }, + }}, + }, + AttributeState: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + }, + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue2"}, + "nested_required": types.String{Value: "testvalue2"}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + }, + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue2"}, + "nested_required": types.String{Value: "testvalue2"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, "block-single-null-plan": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1285,6 +2694,69 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "block-single-null-plan-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSingle, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Null: true, + }, + }, + AttributeState: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Null: true}, + }, + Null: true, + }, + }, + Private: testProviderData, + }, + }, "block-single-null-state": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1338,6 +2810,68 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "block-single-null-state-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSingle, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + AttributeState: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Null: true, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + Private: testProviderData, + }, + }, "block-single-nested-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1382,12 +2916,76 @@ func TestBlockModifyPlan(t *testing.T) { }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.Object{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + AttributePlan: types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + Private: testProviderData, + }, + }, + "block-single-nested-private-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeSingle, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + AttributeState: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, }, }, Private: testProviderData, @@ -1457,6 +3055,79 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "block-single-nested-usestateforunknown-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeSingle, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Null: true}, + "nested_required": types.String{Value: "testvalue"}, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Unknown: true}, + "nested_required": types.String{Value: "testvalue"}, + }, + }, + }, + AttributeState: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue"}, + "nested_required": types.String{Value: "testvalue"}, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue"}, + "nested_required": types.String{Value: "testvalue"}, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, "block-requires-replacement": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1505,6 +3176,59 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "block-requires-replacement-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, nil), + modifyAttributePlanValues{ + config: "newtestvalue", + plan: "newtestvalue", + state: "testvalue", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "newtestvalue"}, + }, + }, + }, + }, + }, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, "block-requires-replacement-passthrough": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1546,6 +3270,50 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "block-requires-replacement-passthrough-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + testBlockPlanModifierNullListCustomType{}, + }, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + testBlockPlanModifierNullListCustomType{}, + }, nil), + modifyAttributePlanValues{ + config: "newtestvalue", + plan: "newtestvalue", + state: "testvalue", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + }, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, "block-requires-replacement-unset": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1649,8 +3417,123 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, - "block-error": { + "block-warnings-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTDIAG", + plan: "TESTDIAG", + state: "TESTDIAG", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + Diagnostics: diag.Diagnostics{ + // Diagnostics.Append() deduplicates, so the warning will only + // be here once unless the test implementation is changed to + // different modifiers or the modifier itself is changed. + diag.NewWarningDiagnostic( + "Warning diag", + "This is a warning", + ), + }, + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTDIAG"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "block-error": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + req: modifyAttributePlanRequest( + path.Root("test"), + schema([]tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTDIAG", + plan: "TESTDIAG", + state: "TESTDIAG", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Error diag", + "This is an error", + ), + }, + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTDIAG"}, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "block-error-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -1663,9 +3546,9 @@ func TestBlockModifyPlan(t *testing.T) { planmodifiers.TestErrorDiagModifier{}, }, }, - req: modifyAttributePlanRequest( + req: modifyAttributePlanRequestCustomType( path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ + schemaCustomType([]tfsdk.AttributePlanModifier{ planmodifiers.TestErrorDiagModifier{}, planmodifiers.TestErrorDiagModifier{}, }, nil), @@ -1683,18 +3566,22 @@ func TestBlockModifyPlan(t *testing.T) { ), }, AttributePlan: types.List{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - Elems: []attr.Value{ - types.Object{ + ElemType: CustomType{ + types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - Attrs: map[string]attr.Value{ - "nested_attr": types.String{Value: "TESTDIAG"}, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTDIAG"}, + }, }, }, }, @@ -1749,6 +3636,58 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "nested-attribute-modified-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "TESTATTRONE", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "MODIFIED_TWO"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, "nested-attribute-requires-replacement": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1847,6 +3786,61 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "nested-attribute-requires-replacement-passthrough-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, + }), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "previousvalue", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTATTRTWO"}, + }, + }, + }, + }, + }, + RequiresReplace: path.Paths{ + path.Root("test").AtListIndex(0).AtName("nested_attr"), + }, + Private: testEmptyProviderData, + }, + }, "nested-attribute-requires-replacement-unset": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1894,6 +3888,58 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "nested-attribute-requires-replacement-unset-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }), + modifyAttributePlanValues{ + config: "newtestvalue", + plan: "newtestvalue", + state: "testvalue", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "newtestvalue"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, "nested-attribute-warnings": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -1950,6 +3996,67 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "nested-attribute-warnings-custom-type": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }), + modifyAttributePlanValues{ + config: "TESTDIAG", + plan: "TESTDIAG", + state: "TESTDIAG", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + Diagnostics: diag.Diagnostics{ + // Diagnostics.Append() deduplicates, so the warning will only + // be here once unless the test implementation is changed to + // different modifiers or the modifier itself is changed. + diag.NewWarningDiagnostic( + "Warning diag", + "This is a warning", + ), + }, + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTDIAG"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, "nested-attribute-error": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -2003,6 +4110,64 @@ func TestBlockModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, + "nested-attribute-error-custom-attribute": { + block: tfsdk.Block{ + Typ: CustomType{}, + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: modifyAttributePlanRequestCustomType( + path.Root("test"), + schemaCustomType(nil, []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }), + modifyAttributePlanValues{ + config: "TESTDIAG", + plan: "TESTDIAG", + state: "TESTDIAG", + }, + ), + expectedResp: ModifyAttributePlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Error diag", + "This is an error", + ), + }, + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Elems: []attr.Value{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "TESTDIAG"}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, } for name, tc := range testCases { @@ -2050,6 +4215,34 @@ func (t testBlockPlanModifierNullList) MarkdownDescription(ctx context.Context) return "This plan modifier is for use during testing only" } +type testBlockPlanModifierNullListCustomType struct{} + +func (t testBlockPlanModifierNullListCustomType) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { + _, ok := req.AttributePlan.(types.List) + if !ok { + return + } + + resp.AttributePlan = types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + }, + Null: true, + } +} + +func (t testBlockPlanModifierNullListCustomType) Description(ctx context.Context) string { + return "This plan modifier is for use during testing only" +} + +func (t testBlockPlanModifierNullListCustomType) MarkdownDescription(ctx context.Context) string { + return "This plan modifier is for use during testing only" +} + type testBlockPlanModifierPrivateGet struct{} func (t testBlockPlanModifierPrivateGet) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { diff --git a/tfsdk/block_test.go b/tfsdk/block_test.go index c056baf2a..b336e04d6 100644 --- a/tfsdk/block_test.go +++ b/tfsdk/block_test.go @@ -30,19 +30,8 @@ func (t CustomType) Equal(candidate attr.Type) bool { if !ok { return false } - if len(other.AttrTypes) != len(t.AttrTypes) { - return false - } - for k, v := range t.AttrTypes { - attrType, ok := other.AttrTypes[k] - if !ok { - return false - } - if !v.Equal(attrType) { - return false - } - } - return true + + return t.ObjectType.Equal(other.ObjectType) } func TestBlockType(t *testing.T) { diff --git a/types/object.go b/types/object.go index 1779dff96..1a79575a2 100644 --- a/types/object.go +++ b/types/object.go @@ -152,6 +152,10 @@ func (t ObjectType) ValueType(_ context.Context) attr.Value { } } +var ( + _ attr.ValueWithAttrs = Object{} +) + // Object represents an object type Object struct { // Unknown will be set to true if the entire object is an unknown value. @@ -172,6 +176,16 @@ type Object struct { AttrTypes map[string]attr.Type } +func (o Object) GetAttrs() map[string]attr.Value { + return o.Attrs +} + +func (o Object) SetAttrs(attrs map[string]attr.Value) attr.ValueWithAttrs { + o.Attrs = attrs + + return o +} + // ObjectAsOptions is a collection of toggles to control the behavior of // Object.As. type ObjectAsOptions struct {