diff --git a/.changelog/479.txt b/.changelog/479.txt new file mode 100644 index 000000000..9b2442596 --- /dev/null +++ b/.changelog/479.txt @@ -0,0 +1,3 @@ +```release-note:bug +internal/fwserver: Ensured nested block plan modifiers correctly set their request `AttributeConfig`, `AttributePlan`, and `AttributeState` values +``` diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index a8de19322..747f8f39b 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -160,13 +160,40 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr } for name, block := range b.GetBlocks() { + attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + attrPlan, diags := objectAttributeValue(ctx, planObject, name, fwschemadata.DataDescriptionPlan) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + attrState, diags := objectAttributeValue(ctx, stateObject, name, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + blockReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtListIndex(idx).AtName(name), - Config: req.Config, - Plan: req.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: req.AttributePath.AtListIndex(idx).AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, } blockResp := ModifyAttributePlanResponse{ AttributePlan: blockReq.AttributePlan, diff --git a/internal/fwserver/block_plan_modification_test.go b/internal/fwserver/block_plan_modification_test.go index 861d21872..8bd32a22d 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -836,6 +836,257 @@ func TestBlockModifyPlan(t *testing.T) { 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, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: 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: types.ObjectType{ + 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{Null: true}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + AttributePath: path.Root("test"), + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: 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: types.ObjectType{ + 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{Unknown: true}, + "nested_required": types.String{Value: "configvalue"}, + }, + }, + }, + }, + }, + }, + }, + }, + AttributeState: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: 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: types.ObjectType{ + 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"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, + }, + }, + }, + }, + }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "list": types.ListType{ + ElemType: 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: types.ObjectType{ + 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-set-nested-usestateforunknown": { block: tfsdk.Block{ Attributes: map[string]tfsdk.Attribute{