From 897a02fd16598d13fc9d7bfb31eabdaf7cad3d34 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 | 60 +- internal/fwserver/block_plan_modification.go | 56 +- .../fwserver/block_plan_modification_test.go | 2529 +++++++++++++++-- tfsdk/block_test.go | 15 +- types/object.go | 14 + 7 files changed, 2441 insertions(+), 256 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 93b7c790d..57db66ae7 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 143f64a38..40a0df84c 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -151,7 +151,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) @@ -197,19 +201,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 } - planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + planList.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan, diags = types.ListValue(planList.ElementType(ctx), planElements) @@ -273,7 +271,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) @@ -319,19 +321,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 } - planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + planSet.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan, diags = types.SetValue(planSet.ElementType(ctx), planElements) @@ -395,7 +391,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) @@ -441,19 +441,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 } - planElements[key], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + planMap.Elems[key] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan, diags = types.MapValue(planMap.ElementType(ctx), planElements) @@ -488,7 +482,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 @@ -540,19 +534,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, diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + 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 aff1ab35d..00b0a576a 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -117,7 +117,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) @@ -163,7 +167,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 @@ -213,19 +217,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 } - planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + planList.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan, diags = types.ListValue(planList.ElementType(ctx), planElements) @@ -289,7 +287,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) @@ -335,7 +337,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 @@ -385,19 +387,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 } - planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + planSet.Elems[idx] = planObject.(attr.ValueWithAttrs).SetAttrs(planObjectAttrs) } resp.AttributePlan, diags = types.SetValue(planSet.ElementType(ctx), planElements) @@ -432,11 +428,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) @@ -482,7 +478,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 @@ -532,19 +528,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, diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes) - - resp.Diagnostics.Append(diags...) - - if resp.Diagnostics.HasError() { - return - } + 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 ca5598b04..eac3faf1e 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,6 +554,58 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "block-request-private-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{ + testBlockPlanModifierPrivateGet{}, + }, + }, + req: modifyAttributePlanWithPrivateRequestCustomType( + path.Root("test"), + schemaCustomType([]tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateGet{}, + }, nil), + modifyAttributePlanValues{ + config: "TESTATTRONE", + plan: "TESTATTRONE", + state: "TESTATTRONE", + }, + testProviderData, + ), + 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: "TESTATTRONE"}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, "block-response-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ @@ -344,33 +652,84 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-list-null-plan": { + "block-response-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{}, - }, + 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{ - 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, + 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, + }, + }, + }, + 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"}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "block-list-null-plan": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: 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: "testvalue"}, @@ -417,8 +776,9 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-list-null-state": { + "block-list-null-plan-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -435,71 +795,74 @@ 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, + 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"}, + }, + }, }, }, - Null: true, }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.ListValueMust( - types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, - }, - }, - []attr.Value{ - types.ObjectValueMust( - map[string]attr.Type{ + AttributePlan: types.List{ + ElemType: CustomType{ + types.ObjectType{ + AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, }, - map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, - }, - ), + }, }, - ), + Null: true, + }, Private: testProviderData, }, }, - "block-list-nested-private": { + "block-list-null-state": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { @@ -557,16 +920,7 @@ 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, }, }, expectedResp: ModifyAttributePlanResponse{ @@ -590,8 +944,9 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-set-null-plan": { + "block-list-null-state-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -601,69 +956,92 @@ 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, + AttributePlan: types.List{ + ElemType: CustomType{ + 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{ + CustomValue{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, + }, + }, }, }, - Elems: []attr.Value{ - types.Object{ + }, + AttributeState: 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"}, - }, }, }, + Null: true, }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.Set{ - ElemType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "nested_attr": types.StringType, + 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"}, + }, + }, }, }, - Null: true, }, Private: testProviderData, }, }, - "block-set-null-state": { + "block-list-nested-private": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{ "nested_attr": { @@ -674,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, @@ -698,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, @@ -715,17 +1093,26 @@ func TestBlockModifyPlan(t *testing.T) { }, }, }, - AttributeState: types.Set{ + AttributeState: 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"}, + }, + }, + }, }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.SetValueMust( + AttributePlan: types.ListValueMust( types.ObjectType{ AttrTypes: map[string]attr.Type{ "nested_attr": types.StringType, @@ -745,8 +1132,9 @@ func TestBlockModifyPlan(t *testing.T) { Private: testProviderData, }, }, - "block-set-nested-private": { + "block-list-nested-private-custom-type": { block: tfsdk.Block{ + Typ: CustomType{}, Attributes: map[string]tfsdk.Attribute{ "nested_attr": { Type: types.StringType, @@ -756,65 +1144,510 @@ 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"}, - }, }, }, - }, - AttributeState: types.Set{ - 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: "testvalue"}, + }, + }, }, }, - Elems: []attr.Value{ - types.Object{ + }, + AttributeState: 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"}, + }, + }, + }, + }, + }, + }, + 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.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + 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.SetValueMust( types.ObjectType{ @@ -836,6 +1669,114 @@ func TestBlockModifyPlan(t *testing.T) { 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{ @@ -1073,17 +2014,318 @@ func TestBlockModifyPlan(t *testing.T) { "nested_computed": types.StringType, "nested_required": types.StringType, }, - map[string]attr.Value{ - "nested_computed": types.String{Value: "statevalue"}, - "nested_required": types.String{Value: "configvalue"}, + 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, + }, + }, + }, + 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"}, + }, + }, }, - ), + }, }, - ), + }, }, - ), + }, }, - ), + }, Private: testEmptyProviderData, }, }, @@ -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{ @@ -1282,6 +2691,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{ @@ -1335,6 +2807,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{ @@ -1379,14 +2913,78 @@ func TestBlockModifyPlan(t *testing.T) { }, }, expectedResp: ModifyAttributePlanResponse{ - AttributePlan: types.ObjectValueMust( - map[string]attr.Type{ - "nested_attr": types.StringType, - }, - map[string]attr.Value{ - "nested_attr": types.String{Value: "testvalue"}, + AttributePlan: types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + 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, }, }, @@ -1454,6 +3052,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{ @@ -1502,6 +3173,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{ @@ -1543,6 +3267,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{ @@ -1646,8 +3414,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, @@ -1660,9 +3543,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), @@ -1680,18 +3563,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"}, + }, }, }, }, @@ -1746,6 +3633,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{ @@ -1844,6 +3783,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{ @@ -1891,6 +3885,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{ @@ -1947,6 +3993,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{ @@ -2000,6 +4107,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 { @@ -2047,6 +4212,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 65f929714..9aa66a559 100644 --- a/types/object.go +++ b/types/object.go @@ -300,6 +300,10 @@ func ObjectValueMust(attributeTypes map[string]attr.Type, attributes map[string] return object } +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. @@ -361,6 +365,16 @@ type Object struct { state valueState } +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 {