diff --git a/.changelog/468.txt b/.changelog/468.txt new file mode 100644 index 000000000..c1170bbf4 --- /dev/null +++ b/.changelog/468.txt @@ -0,0 +1,3 @@ +```release-note:bug +internal/fwserver: Fixed alignment of set type plan modification +``` diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go new file mode 100644 index 000000000..26b5a2fb2 --- /dev/null +++ b/internal/fwserver/attr_value.go @@ -0,0 +1,176 @@ +package fwserver + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func coerceListValue(schemaPath path.Path, value attr.Value) (types.List, diag.Diagnostics) { + list, ok := value.(types.List) + + if !ok { + return types.List{Null: true}, diag.Diagnostics{ + attributePlanModificationWalkError(schemaPath, value), + } + } + + return list, nil +} + +func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) { + m, ok := value.(types.Map) + + if !ok { + return types.Map{Null: true}, diag.Diagnostics{ + attributePlanModificationWalkError(schemaPath, value), + } + } + + return m, nil +} + +func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { + object, ok := value.(types.Object) + + if !ok { + return types.Object{Null: true}, diag.Diagnostics{ + attributePlanModificationWalkError(schemaPath, value), + } + } + + return object, nil +} + +func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { + set, ok := value.(types.Set) + + if !ok { + return types.Set{Null: true}, diag.Diagnostics{ + attributePlanModificationWalkError(schemaPath, value), + } + } + + return set, nil +} + +func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { + if list.IsNull() { + return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, nil) + } + + if list.IsUnknown() { + return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, tftypes.UnknownValue) + } + + if index > len(list.Elems) { + return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, nil) + } + + return coerceObjectValue(schemaPath, list.Elems[index]) +} + +func listElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, list types.List, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { + elemValue, err := list.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(list.ElemType.TerraformType(ctx), tfValue)) + + if err != nil { + return types.Object{Null: true}, diag.Diagnostics{ + attributePlanModificationValueError(ctx, list, description, err), + } + } + + return coerceObjectValue(schemaPath, elemValue) +} + +func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key string, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { + if m.IsNull() { + return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, nil) + } + + if m.IsUnknown() { + return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, tftypes.UnknownValue) + } + + elemValue, ok := m.Elems[key] + + if !ok { + return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, nil) + } + + return coerceObjectValue(schemaPath, elemValue) +} + +func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, m types.Map, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { + elemValue, err := m.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(m.ElemType.TerraformType(ctx), tfValue)) + + if err != nil { + return types.Object{Null: true}, diag.Diagnostics{ + attributePlanModificationValueError(ctx, m, description, err), + } + } + + return coerceObjectValue(schemaPath, elemValue) +} + +func objectAttributeValue(ctx context.Context, object types.Object, attributeName string, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { + if object.IsNull() { + return objectAttributeValueFromTerraformValue(ctx, object, attributeName, description, nil) + } + + if object.IsUnknown() { + return objectAttributeValueFromTerraformValue(ctx, object, attributeName, description, tftypes.UnknownValue) + } + + // A panic here indicates a bug somewhere else in the framework or an + // invalid test case. + return object.Attrs[attributeName], nil +} + +func objectAttributeValueFromTerraformValue(ctx context.Context, object types.Object, attributeName string, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { + // A panic here indicates a bug somewhere else in the framework or an + // invalid test case. + attrType := object.AttrTypes[attributeName] + + elemValue, err := attrType.ValueFromTerraform(ctx, tftypes.NewValue(attrType.TerraformType(ctx), tfValue)) + + if err != nil { + return nil, diag.Diagnostics{ + attributePlanModificationValueError(ctx, object, description, err), + } + } + + return elemValue, nil +} + +func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { + if set.IsNull() { + return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, nil) + } + + if set.IsUnknown() { + return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, tftypes.UnknownValue) + } + + if index > len(set.Elems) { + return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, nil) + } + + return coerceObjectValue(schemaPath, set.Elems[index]) +} + +func setElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, set types.Set, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { + elemValue, err := set.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(set.ElemType.TerraformType(ctx), tfValue)) + + if err != nil { + return types.Object{Null: true}, diag.Diagnostics{ + attributePlanModificationValueError(ctx, set, description, err), + } + } + + return coerceObjectValue(schemaPath, elemValue) +} diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index 1bda63ec3..32281b42c 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -4,69 +4,33 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema" "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/logging" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-framework/types" ) +type ModifyAttributePlanResponse struct { + AttributePlan attr.Value + Diagnostics diag.Diagnostics + RequiresReplace path.Paths + Private *privatestate.ProviderData +} + // AttributeModifyPlan runs all AttributePlanModifiers // // TODO: Clean up this abstraction back into an internal Attribute type method. // The extra Attribute parameter is a carry-over of creating the proto6server // package from the tfsdk package and not wanting to export the method. // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/365 -func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.ModifyAttributePlanRequest, resp *ModifySchemaPlanResponse) { +func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.ModifyAttributePlanRequest, resp *ModifyAttributePlanResponse) { ctx = logging.FrameworkWithAttributePath(ctx, req.AttributePath.String()) - configData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionConfiguration, - Schema: req.Config.Schema, - TerraformValue: req.Config.Raw, - } - - attrConfig, diags := configData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - // Only on new errors. - if diags.HasError() { - return - } - req.AttributeConfig = attrConfig - - stateData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionState, - Schema: req.State.Schema, - TerraformValue: req.State.Raw, - } - - attrState, diags := stateData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - // Only on new errors. - if diags.HasError() { - return - } - req.AttributeState = attrState - - planData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionPlan, - Schema: req.Plan.Schema, - TerraformValue: req.Plan.Raw, - } - - attrPlan, diags := planData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - // Only on new errors. - if diags.HasError() { - return - } - req.AttributePlan = attrPlan - var requiresReplace bool privateProviderData := privatestate.EmptyProviderData(ctx) @@ -103,6 +67,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo req.AttributePlan = modifyResp.AttributePlan resp.Diagnostics.Append(modifyResp.Diagnostics...) requiresReplace = modifyResp.RequiresReplace + resp.AttributePlan = modifyResp.AttributePlan resp.Private = modifyResp.Private // Only on new errors. @@ -116,10 +81,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo resp.RequiresReplace = append(resp.RequiresReplace, req.AttributePath) } - setAttrDiags := resp.Plan.SetAttribute(ctx, req.AttributePath, req.AttributePlan) - resp.Diagnostics.Append(setAttrDiags...) - - if setAttrDiags.HasError() { + if resp.Diagnostics.HasError() { return } @@ -130,119 +92,403 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo nm := a.GetAttributes().GetNestingMode() switch nm { case fwschema.NestingModeList: - l, ok := req.AttributePlan.(types.List) - - if !ok { - err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Attribute Plan Modification Error", - "Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + configList, diags := coerceListValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { return } - for idx := range l.Elems { + planList, diags := coerceListValue(req.AttributePath, req.AttributePlan) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateList, diags := coerceListValue(req.AttributePath, req.AttributeState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + for idx, planElem := range planList.Elems { + attrPath := req.AttributePath.AtListIndex(idx) + + configObject, diags := listElemObject(ctx, attrPath, configList, idx, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(attrPath, planElem) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := listElemObject(ctx, attrPath, stateList, idx, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + for name, attr := range a.GetAttributes().GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtListIndex(idx).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } + + planList.Elems[idx] = planObject } + + resp.AttributePlan = planList case fwschema.NestingModeSet: - s, ok := req.AttributePlan.(types.Set) - - if !ok { - err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Attribute Plan Modification Error", - "Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + configSet, diags := coerceSetValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { return } - for _, value := range s.Elems { + planSet, diags := coerceSetValue(req.AttributePath, req.AttributePlan) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateSet, diags := coerceSetValue(req.AttributePath, req.AttributeState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + for idx, planElem := range planSet.Elems { + attrPath := req.AttributePath.AtSetValue(planElem) + + configObject, diags := setElemObject(ctx, attrPath, configSet, idx, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(attrPath, planElem) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := setElemObject(ctx, attrPath, stateSet, idx, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + for name, attr := range a.GetAttributes().GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtSetValue(value).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } + + planSet.Elems[idx] = planObject } + + resp.AttributePlan = planSet case fwschema.NestingModeMap: - m, ok := req.AttributePlan.(types.Map) - - if !ok { - err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Attribute Plan Modification Error", - "Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + configMap, diags := coerceMapValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planMap, diags := coerceMapValue(req.AttributePath, req.AttributePlan) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { return } - for key := range m.Elems { + stateMap, diags := coerceMapValue(req.AttributePath, req.AttributeState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + for key, planElem := range planMap.Elems { + attrPath := req.AttributePath.AtMapKey(key) + + configObject, diags := mapElemObject(ctx, attrPath, configMap, key, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(attrPath, planElem) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := mapElemObject(ctx, attrPath, stateMap, key, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + for name, attr := range a.GetAttributes().GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtMapKey(key).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } + + planMap.Elems[key] = planObject } + + resp.AttributePlan = planMap case fwschema.NestingModeSingle: - o, ok := req.AttributePlan.(types.Object) - - if !ok { - err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributePlan, nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Attribute Plan Modification Error", - "Attribute plan modifier cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + configObject, diags := coerceObjectValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(req.AttributePath, req.AttributePlan) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := coerceObjectValue(req.AttributePath, req.AttributeState) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { return } - if len(o.Attrs) == 0 { + if len(planObject.Attrs) == 0 { return } for name, attr := range a.GetAttributes().GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: req.AttributePath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } + + resp.AttributePlan = planObject default: err := fmt.Errorf("unknown attribute nesting mode (%T: %v) at path: %s", nm, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( @@ -254,3 +500,22 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } } + +func attributePlanModificationValueError(ctx context.Context, value attr.Value, description fwschemadata.DataDescription, err error) diag.Diagnostic { + return diag.NewErrorDiagnostic( + "Attribute Plan Modification "+description.Title()+" Value Error", + "An unexpected error occurred while fetching a "+value.Type(ctx).String()+" element value in the "+description.String()+". "+ + "This is an issue with the provider and should be reported to the provider developers.\n\n"+ + "Original Error: "+err.Error(), + ) +} + +func attributePlanModificationWalkError(schemaPath path.Path, value attr.Value) diag.Diagnostic { + return diag.NewAttributeErrorDiagnostic( + schemaPath, + "Attribute Plan Modification Walk Error", + "An unexpected error occurred while walking the schema for attribute plan modification. "+ + "This is an issue with terraform-plugin-framework and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("unknown attribute value type (%T) at path: %s", value, schemaPath), + ) +} diff --git a/internal/fwserver/attribute_plan_modification_test.go b/internal/fwserver/attribute_plan_modification_test.go index e6f78fee4..12f0082f4 100644 --- a/internal/fwserver/attribute_plan_modification_test.go +++ b/internal/fwserver/attribute_plan_modification_test.go @@ -5,15 +5,17 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/testing/planmodifiers" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestAttributeModifyPlan(t *testing.T) { @@ -28,2360 +30,403 @@ func TestAttributeModifyPlan(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) testCases := map[string]struct { + attribute fwschema.Attribute req tfsdk.ModifyAttributePlanRequest - resp ModifySchemaPlanResponse // Plan automatically copied from req - expectedResp ModifySchemaPlanResponse + expectedResp ModifyAttributePlanResponse }{ - "config-error": { + "no-plan-modifiers": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + }, req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, + AttributeConfig: types.String{Value: "testvalue"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "testvalue"}, + AttributeState: types.String{Value: "testvalue"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "testvalue"}, + }, + }, + "attribute-plan": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTATTRONE"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTATTRONE"}, + AttributeState: types.String{Value: "TESTATTRONE"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "MODIFIED_TWO"}, + Private: testEmptyProviderData, + }, + }, + "attribute-request-private": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, }, }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Configuration Read Error", - "An unexpected error was encountered trying to convert an attribute value from the configuration. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTATTRONE"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTATTRONE"}, + AttributeState: types.String{Value: "TESTATTRONE"}, + Private: testProviderData, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "TESTATTRONE"}, + Private: testProviderData, + }, + }, + "attribute-response-private": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTATTRONE"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTATTRONE"}, + AttributeState: types.String{Value: "TESTATTRONE"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "TESTATTRONE"}, + Private: testProviderData, + }, + }, + "attribute-list-nested-private": { + attribute: tfsdk.Attribute{ + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, }, }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, }, }, - }, - "config-error-previous-error": { req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeConfig: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: 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"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributePath: path.Root("test"), + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: 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"}, }, }, }, }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeState: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: 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"}, }, }, }, }, }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Configuration Read Error", - "An unexpected error was encountered trying to convert an attribute value from the configuration. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: 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, }, }, - "plan-error": { + "attribute-set-nested-private": { + attribute: tfsdk.Attribute{ + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: 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"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: 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"}, }, }, }, }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: 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"}, }, }, }, }, }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Plan Read Error", - "An unexpected error was encountered trying to convert an attribute value from the plan. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: 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, }, }, - "plan-error-previous-error": { + "attribute-set-nested-usestateforunknown": { + attribute: tfsdk.Attribute{ + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }), + Required: true, + }, req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, + }, + 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: "testvalue1"}, + }, + }, + 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"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, + }, + 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: "testvalue1"}, }, }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "Plan Read Error", - "An unexpected error was encountered trying to convert an attribute value from the plan. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, - }, - }, - }, - }, - }, - }, - "state-error": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "State Read Error", - "An unexpected error was encountered trying to convert an attribute value from the state. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - }, - }, - "state-error-previous-error": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.ListType{ElemType: types.StringType}, - Required: true, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - diag.NewAttributeErrorDiagnostic( - path.Root("test"), - "State Read Error", - "An unexpected error was encountered trying to convert an attribute value from the state. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ - "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - }, - }, - "no-plan-modifiers": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - }, - }, - "attribute-plan": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "MODIFIED_TWO"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - Private: testEmptyProviderData, - }, - }, - "attribute-request-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-response-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-list-nested-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-set-nested-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-map-nested-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - map[string]tftypes.Value{ - "testkey": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - map[string]tftypes.Value{ - "testkey": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - map[string]tftypes.Value{ - "testkey": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Map{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - map[string]tftypes.Value{ - "testkey": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-single-nested-private": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "testing": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "testing": { - Type: types.StringType, - Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "testing": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "testing": { - Type: types.StringType, - Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "testing": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "testing": { - Type: types.StringType, - Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "testing": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "testing": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ - "testing": { - Type: types.StringType, - Optional: true, - PlanModifiers: tfsdk.AttributePlanModifiers{ - planmodifiers.TestAttrPlanPrivateModifierGet{}, - }, - }, - }), - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanPrivateModifierSet{}, - }, - }, - }, - }, - }, - Private: testProviderData, - }, - }, - "attribute-plan-previous-error": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "MODIFIED_TWO"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }, - }, - }, - }, - }, - Private: testEmptyProviderData, - }, - }, - "requires-replacement": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - RequiresReplace: path.Paths{ - path.Root("test"), - }, - Private: testEmptyProviderData, - }, - }, - "requires-replacement-previous-error": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "newtestvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - RequiresReplace: path.Paths{ - path.Root("test"), - }, - Private: testEmptyProviderData, - }, - }, - "requires-replacement-passthrough": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - resource.RequiresReplace(), - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestAttrPlanValueModifierOne{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestAttrPlanValueModifierOne{}, - }, - }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTATTRTWO"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - }, - }, - }, - }, - RequiresReplace: path.Paths{ - path.Root("test"), - }, - Private: testEmptyProviderData, - }, - }, - "requires-replacement-unset": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestRequiresReplaceFalseModifier{}, - }, - }, - }, - }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestRequiresReplaceFalseModifier{}, - }, - }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestRequiresReplaceFalseModifier{}, - }, + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "testvalue"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Unknown: true}, + "nested_required": types.String{Value: "testvalue2"}, }, }, }, }, - Private: testEmptyProviderData, - }, - }, - "warnings": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, - }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, }, - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - }, - }, - }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + Attrs: map[string]attr.Value{ + "nested_computed": types.String{Value: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, }, }, - }, - }, - }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - 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", - ), + 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"}, + }, + }, + }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + }, + 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: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + 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"}, }, }, }, @@ -2389,150 +434,174 @@ func TestAttributeModifyPlan(t *testing.T) { Private: testEmptyProviderData, }, }, - "warnings-previous-error": { + "attribute-map-nested-private": { + attribute: tfsdk.Attribute{ + Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeConfig: types.Map{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + }, + Elems: map[string]attr.Value{ + "testkey": types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributePath: path.Root("test"), + AttributePlan: types.Map{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + }, + Elems: map[string]attr.Value{ + "testkey": types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, }, }, }, }, - State: tfsdk.State{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + AttributeState: types.Map{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + }, + Elems: map[string]attr.Value{ + "testkey": types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, }, }, }, }, }, - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - // 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", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Map{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, + }, + Elems: map[string]attr.Value{ + "testkey": types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: "testvalue"}, }, }, }, }, - Private: testEmptyProviderData, + Private: testProviderData, }, }, - "error": { + "attribute-single-nested-private": { + attribute: tfsdk.Attribute{ + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.Object{ + AttrTypes: map[string]attr.Type{ + "testing": types.StringType, + }, + Attrs: map[string]attr.Value{ + "testing": types.String{Value: "testvalue"}, + }, + }, AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, - }, - }, + AttributePlan: types.Object{ + AttrTypes: map[string]attr.Type{ + "testing": types.StringType, + }, + Attrs: map[string]attr.Value{ + "testing": types.String{Value: "testvalue"}, + }, + }, + AttributeState: types.Object{ + AttrTypes: map[string]attr.Type{ + "testing": types.StringType, + }, + Attrs: map[string]attr.Value{ + "testing": types.String{Value: "testvalue"}, + }, + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Object{ + AttrTypes: map[string]attr.Type{ + "testing": types.StringType, }, + Attrs: map[string]attr.Value{ + "testing": types.String{Value: "testvalue"}, + }, + }, + Private: testProviderData, + }, + }, + "requires-replacement": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "newtestvalue"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "newtestvalue"}, + AttributeState: types.String{Value: "testvalue"}, + // resource.RequiresReplace() requires non-null plan + // and state. Plan: tfsdk.Plan{ Raw: tftypes.NewValue(tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test": tftypes.String, }, }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), }), Schema: tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ @@ -2540,8 +609,7 @@ func TestAttributeModifyPlan(t *testing.T) { Type: types.StringType, Required: true, PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, + resource.RequiresReplace(), }, }, }, @@ -2553,7 +621,7 @@ func TestAttributeModifyPlan(t *testing.T) { "test": tftypes.String, }, }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + "test": tftypes.NewValue(tftypes.String, "testvalue"), }), Schema: tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ @@ -2561,77 +629,44 @@ func TestAttributeModifyPlan(t *testing.T) { Type: types.StringType, Required: true, PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, + resource.RequiresReplace(), }, }, }, }, }, }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Error diag", - "This is an error", - ), - }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, - }, - }, - }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "newtestvalue"}, + RequiresReplace: path.Paths{ + path.Root("test"), }, Private: testEmptyProviderData, }, }, - "error-previous-error": { - req: tfsdk.ModifyAttributePlanRequest{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, - }, - }, - }, + "requires-replacement-passthrough": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + resource.RequiresReplace(), }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTATTRONE"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTATTRONE"}, + AttributeState: types.String{Value: "TESTATTRONE"}, + // resource.RequiresReplace() requires non-null plan + // and state. Plan: tfsdk.Plan{ Raw: tftypes.NewValue(tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ "test": tftypes.String, }, }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), }), Schema: tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ @@ -2639,8 +674,8 @@ func TestAttributeModifyPlan(t *testing.T) { Type: types.StringType, Required: true, PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, }, }, }, @@ -2652,7 +687,7 @@ func TestAttributeModifyPlan(t *testing.T) { "test": tftypes.String, }, }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), }), Schema: tfsdk.Schema{ Attributes: map[string]tfsdk.Attribute{ @@ -2660,54 +695,94 @@ func TestAttributeModifyPlan(t *testing.T) { Type: types.StringType, Required: true, PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, }, }, }, }, }, }, - resp: ModifySchemaPlanResponse{ + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "TESTATTRTWO"}, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, + "requires-replacement-unset": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "testvalue"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "testvalue"}, + AttributeState: types.String{Value: "testvalue"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "testvalue"}, + Private: testEmptyProviderData, + }, + }, + "warnings": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTDIAG"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTDIAG"}, + AttributeState: types.String{Value: "TESTDIAG"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "TESTDIAG"}, Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", + // 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", ), }, + Private: testEmptyProviderData, + }, + }, + "error": { + attribute: tfsdk.Attribute{ + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, }, - expectedResp: ModifySchemaPlanResponse{ + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.String{Value: "TESTDIAG"}, + AttributePath: path.Root("test"), + AttributePlan: types.String{Value: "TESTDIAG"}, + AttributeState: types.String{Value: "TESTDIAG"}, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.String{Value: "TESTDIAG"}, Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), diag.NewErrorDiagnostic( "Error diag", "This is an error", ), }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue(tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.String, - }, - }, map[string]tftypes.Value{ - "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), - }), - Schema: tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "test": { - Type: types.StringType, - Required: true, - PlanModifiers: []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, - }, - }, - }, - }, Private: testEmptyProviderData, }, }, @@ -2719,17 +794,14 @@ func TestAttributeModifyPlan(t *testing.T) { t.Parallel() ctx := context.Background() - attribute, diags := tc.req.Config.Schema.AttributeAtPath(ctx, tc.req.AttributePath) - - if diags.HasError() { - t.Fatalf("Unexpected diagnostics: %s", diags) + got := ModifyAttributePlanResponse{ + AttributePlan: tc.req.AttributePlan, + Private: tc.req.Private, } - tc.resp.Plan = tc.req.Plan - - AttributeModifyPlan(ctx, attribute, tc.req, &tc.resp) + AttributeModifyPlan(ctx, tc.attribute, tc.req, &got) - if diff := cmp.Diff(tc.expectedResp, tc.resp, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { + if diff := cmp.Diff(tc.expectedResp, got, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("Unexpected response (-wanted, +got): %s", diff) } }) diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index 845e7cd04..04302d889 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-framework/types" ) // BlockModifyPlan performs all Block plan modification. @@ -18,52 +17,7 @@ import ( // The extra Block parameter is a carry-over of creating the proto6server // package from the tfsdk package and not wanting to export the method. // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/365 -func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttributePlanRequest, resp *ModifySchemaPlanResponse) { - configData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionConfiguration, - Schema: req.Config.Schema, - TerraformValue: req.Config.Raw, - } - - attributeConfig, diags := configData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - if diags.HasError() { - return - } - - req.AttributeConfig = attributeConfig - - planData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionPlan, - Schema: req.Plan.Schema, - TerraformValue: req.Plan.Raw, - } - - attributePlan, diags := planData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - if diags.HasError() { - return - } - - req.AttributePlan = attributePlan - - stateData := &fwschemadata.Data{ - Description: fwschemadata.DataDescriptionState, - Schema: req.State.Schema, - TerraformValue: req.State.Raw, - } - - attributeState, diags := stateData.ValueAtPath(ctx, req.AttributePath) - resp.Diagnostics.Append(diags...) - - if diags.HasError() { - return - } - - req.AttributeState = attributeState - +func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttributePlanRequest, resp *ModifyAttributePlanResponse) { var requiresReplace bool privateProviderData := privatestate.EmptyProviderData(ctx) @@ -86,6 +40,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr req.AttributePlan = modifyResp.AttributePlan resp.Diagnostics.Append(modifyResp.Diagnostics...) requiresReplace = modifyResp.RequiresReplace + resp.AttributePlan = modifyResp.AttributePlan resp.Private = modifyResp.Private // Only on new errors. @@ -99,97 +54,293 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr resp.RequiresReplace = append(resp.RequiresReplace, req.AttributePath) } - setAttrDiags := resp.Plan.SetAttribute(ctx, req.AttributePath, req.AttributePlan) - resp.Diagnostics.Append(setAttrDiags...) - - if setAttrDiags.HasError() { - return - } - nm := b.GetNestingMode() switch nm { case fwschema.BlockNestingModeList: - l, ok := req.AttributePlan.(types.List) + configList, diags := coerceListValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planList, diags := coerceListValue(req.AttributePath, req.AttributePlan) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateList, diags := coerceListValue(req.AttributePath, req.AttributeState) - if !ok { - err := fmt.Errorf("unknown block value type (%s) for nesting mode (%T) at path: %s", req.AttributeConfig.Type(ctx), nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Block Plan Modification Error", - "Block validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { return } - for idx := range l.Elems { + for idx, planElem := range planList.Elems { + attrPath := req.AttributePath.AtListIndex(idx) + + configObject, diags := listElemObject(ctx, attrPath, configList, idx, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(attrPath, planElem) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := listElemObject(ctx, attrPath, stateList, idx, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + for name, attr := range b.GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtListIndex(idx).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } for name, block := range b.GetBlocks() { blockReq := tfsdk.ModifyAttributePlanRequest{ AttributePath: req.AttributePath.AtListIndex(idx).AtName(name), Config: req.Config, - Plan: resp.Plan, + Plan: req.Plan, ProviderMeta: req.ProviderMeta, State: req.State, Private: resp.Private, } + blockResp := ModifyAttributePlanResponse{ + AttributePlan: blockReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: blockReq.Private, + } + + BlockModifyPlan(ctx, block, blockReq, &blockResp) - BlockModifyPlan(ctx, block, blockReq, resp) + planObject.Attrs[name] = blockResp.AttributePlan + resp.Diagnostics.Append(blockResp.Diagnostics...) + resp.RequiresReplace = blockResp.RequiresReplace + resp.Private = blockResp.Private } + + planList.Elems[idx] = planObject } + + resp.AttributePlan = planList case fwschema.BlockNestingModeSet: - s, ok := req.AttributePlan.(types.Set) + configSet, diags := coerceSetValue(req.AttributePath, req.AttributeConfig) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } - if !ok { - err := fmt.Errorf("unknown block value type (%s) for nesting mode (%T) at path: %s", req.AttributeConfig.Type(ctx), nm, req.AttributePath) - resp.Diagnostics.AddAttributeError( - req.AttributePath, - "Block Plan Modification Error", - "Block plan modification cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), - ) + planSet, diags := coerceSetValue(req.AttributePath, req.AttributePlan) + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { return } - for _, value := range s.Elems { + stateSet, diags := coerceSetValue(req.AttributePath, req.AttributeState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + for idx, planElem := range planSet.Elems { + attrPath := req.AttributePath.AtSetValue(planElem) + + configObject, diags := setElemObject(ctx, attrPath, configSet, idx, fwschemadata.DataDescriptionConfiguration) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + planObject, diags := coerceObjectValue(attrPath, planElem) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + + stateObject, diags := setElemObject(ctx, attrPath, stateSet, idx, fwschemadata.DataDescriptionState) + + resp.Diagnostics.Append(diags...) + + if resp.Diagnostics.HasError() { + return + } + for name, attr := range b.GetAttributes() { + 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 + } + attrReq := tfsdk.ModifyAttributePlanRequest{ - AttributePath: req.AttributePath.AtSetValue(value).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.AtName(name), + AttributePlan: attrPlan, + AttributeState: attrState, + Config: req.Config, + Plan: req.Plan, + ProviderMeta: req.ProviderMeta, + State: req.State, + Private: resp.Private, + } + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + RequiresReplace: resp.RequiresReplace, + Private: attrReq.Private, } - AttributeModifyPlan(ctx, attr, attrReq, resp) + AttributeModifyPlan(ctx, attr, attrReq, &attrResp) + + planObject.Attrs[name] = attrResp.AttributePlan + resp.Diagnostics.Append(attrResp.Diagnostics...) + resp.RequiresReplace = attrResp.RequiresReplace + resp.Private = attrResp.Private } 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.AtSetValue(value).AtName(name), - Config: req.Config, - Plan: resp.Plan, - ProviderMeta: req.ProviderMeta, - State: req.State, - Private: resp.Private, + AttributeConfig: attrConfig, + AttributePath: attrPath.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, + RequiresReplace: resp.RequiresReplace, + Private: blockReq.Private, + } + + BlockModifyPlan(ctx, block, blockReq, &blockResp) - BlockModifyPlan(ctx, block, blockReq, resp) + planObject.Attrs[name] = blockResp.AttributePlan + resp.Diagnostics.Append(blockResp.Diagnostics...) + resp.RequiresReplace = blockResp.RequiresReplace + resp.Private = blockResp.Private } + + planSet.Elems[idx] = planObject } + + resp.AttributePlan = planSet 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 74ec393bc..2f9a259f7 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/testing/planmodifiers" "github.com/hashicorp/terraform-plugin-framework/path" @@ -38,24 +39,6 @@ func TestBlockModifyPlan(t *testing.T) { } } - schemaSet := func(blockPlanModifiers tfsdk.AttributePlanModifiers, nestedAttrPlanModifiers tfsdk.AttributePlanModifiers) tfsdk.Schema { - return tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - Attributes: map[string]tfsdk.Attribute{ - "nested_attr": { - Type: types.StringType, - Required: true, - PlanModifiers: nestedAttrPlanModifiers, - }, - }, - NestingMode: tfsdk.BlockNestingModeSet, - PlanModifiers: blockPlanModifiers, - }, - }, - } - } - schemaTfValue := func(nestedAttrValue string) tftypes.Value { return tftypes.NewValue( tftypes.Object{ @@ -95,80 +78,66 @@ func TestBlockModifyPlan(t *testing.T) { ) } - schemaTfValueSet := func(nestedAttrValue string) tftypes.Value { - return tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, + type modifyAttributePlanValues struct { + config string + plan string + state string + } + + modifyAttributePlanRequest := func(attrPath path.Path, schema tfsdk.Schema, values modifyAttributePlanValues) tfsdk.ModifyAttributePlanRequest { + return tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, + Elems: []attr.Value{ + types.Object{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + Attrs: map[string]attr.Value{ + "nested_attr": types.String{Value: values.config}, }, }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, nestedAttrValue), - }, - ), - }, - ), + }, }, - ) - } - - var schemaNullTfValue tftypes.Value = tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, + AttributePath: attrPath, + 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: values.plan}, }, }, }, }, - }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, + AttributeState: 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: values.state}, }, }, }, - nil, - ), - }, - ) - - type modifyAttributePlanValues struct { - config string - plan string - state string - } - - modifyAttributePlanRequest := func(attrPath path.Path, schema tfsdk.Schema, values modifyAttributePlanValues) tfsdk.ModifyAttributePlanRequest { - return tfsdk.ModifyAttributePlanRequest{ - AttributePath: attrPath, + }, Config: tfsdk.Config{ Raw: schemaTfValue(values.config), Schema: schema, @@ -200,11 +169,22 @@ func TestBlockModifyPlan(t *testing.T) { testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) testCases := map[string]struct { + block fwschema.Block req tfsdk.ModifyAttributePlanRequest - resp ModifySchemaPlanResponse // Plan automatically copied from req - expectedResp ModifySchemaPlanResponse + expectedResp ModifyAttributePlanResponse }{ "no-plan-modifiers": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: nil, + }, req: modifyAttributePlanRequest( path.Root("test"), schema(nil, nil), @@ -214,15 +194,40 @@ func TestBlockModifyPlan(t *testing.T) { state: "testvalue", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("testvalue"), - Schema: schema(nil, nil), + expectedResp: ModifyAttributePlanResponse{ + 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: "testvalue"}, + }, + }, + }, }, }, }, "block-modified": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierNullList{}, + }, + }, req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -234,18 +239,32 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTATTRONE", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaNullTfValue, - Schema: schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierNullList{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: 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{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateGet{}, + }, + }, req: modifyAttributePlanWithPrivateRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -258,18 +277,41 @@ func TestBlockModifyPlan(t *testing.T) { }, testProviderData, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTATTRONE"), - Schema: schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateGet{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + 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: "TESTATTRONE"}, + }, + }, + }, }, Private: testProviderData, }, }, "block-response-private": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: nil, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + testBlockPlanModifierPrivateSet{}, + }, + }, req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -281,455 +323,366 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTATTRONE", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTATTRONE"), - Schema: schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateSet{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + 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: "TESTATTRONE"}, + }, + }, + }, }, Private: testProviderData, }, }, "block-list-nested-private": { + 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"}, + }, + }, + }, + }, AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, + 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: "testvalue"}, }, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), + }, + }, + AttributeState: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + }, + 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"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, + }, + expectedResp: ModifyAttributePlanResponse{ + 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: "testvalue"}, }, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "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{}, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + }, + }, + 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"}, }, }, }, }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, + 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"}, }, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.List{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), + }, + }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + }, + 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"}, }, }, }, }, }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("testvalue"), - Schema: schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateSet{}, - }, []tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateGet{}, - }), + 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": { + "block-set-nested-usestateforunknown": { + block: tfsdk.Block{ + 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{ - AttributePath: path.Root("test"), - Config: tfsdk.Config{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, + AttributeConfig: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), + }, + 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: "testvalue1"}, + }, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + 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"}, }, }, }, }, - Plan: tfsdk.Plan{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, + AttributePath: path.Root("test"), + AttributePlan: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), + }, + 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: "testvalue1"}, + }, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + 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"}, }, }, }, }, - State: tfsdk.State{ - Raw: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "test": tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - }, + AttributeState: types.Set{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_computed": types.StringType, + "nested_required": types.StringType, }, - map[string]tftypes.Value{ - "test": tftypes.NewValue( - tftypes.Set{ - ElementType: tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - }, - []tftypes.Value{ - tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "nested_attr": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), - }, - ), - }, - ), + }, + 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: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, + }, }, - ), - Schema: tfsdk.Schema{ - Blocks: map[string]tfsdk.Block{ - "test": { - 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{}, - }, + 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"}, }, }, }, }, }, - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValueSet("testvalue"), - Schema: schemaSet([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateSet{}, - }, []tfsdk.AttributePlanModifier{ - testBlockPlanModifierPrivateGet{}, - }), - }, - Private: testProviderData, - }, - }, - "block-modified-previous-error": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierNullList{}, - }, nil), - modifyAttributePlanValues{ - config: "TESTATTRONE", - plan: "TESTATTRONE", - state: "TESTATTRONE", - }, - ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaNullTfValue, - Schema: schema([]tfsdk.AttributePlanModifier{ - testBlockPlanModifierNullList{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.Set{ + 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: "statevalue1"}, + "nested_required": types.String{Value: "testvalue1"}, + }, + }, + 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-requires-replacement": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, nil), - modifyAttributePlanValues{ - config: "newtestvalue", - plan: "newtestvalue", - state: "testvalue", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema([]tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, nil), + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, }, - RequiresReplace: path.Paths{ - path.Root("test"), + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), }, - Private: testEmptyProviderData, }, - }, - "block-requires-replacement-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -738,29 +691,26 @@ func TestBlockModifyPlan(t *testing.T) { modifyAttributePlanValues{ config: "newtestvalue", plan: "newtestvalue", - state: "testvalue", - }, - ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), + state: "testvalue", }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema([]tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }, nil), + ), + expectedResp: ModifyAttributePlanResponse{ + 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: "newtestvalue"}, + }, + }, + }, }, RequiresReplace: path.Paths{ path.Root("test"), @@ -769,6 +719,19 @@ func TestBlockModifyPlan(t *testing.T) { }, }, "block-requires-replacement-passthrough": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + testBlockPlanModifierNullList{}, + }, + }, req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -781,14 +744,14 @@ func TestBlockModifyPlan(t *testing.T) { state: "testvalue", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaNullTfValue, - Schema: schema([]tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - testBlockPlanModifierNullList{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.List{ + ElemType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + Null: true, }, RequiresReplace: path.Paths{ path.Root("test"), @@ -797,6 +760,19 @@ func TestBlockModifyPlan(t *testing.T) { }, }, "block-requires-replacement-unset": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -809,53 +785,41 @@ func TestBlockModifyPlan(t *testing.T) { state: "testvalue", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema([]tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestRequiresReplaceFalseModifier{}, - }, nil), + expectedResp: ModifyAttributePlanResponse{ + 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: "newtestvalue"}, + }, + }, + }, }, Private: testEmptyProviderData, }, }, "block-warnings": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + PlanModifiers: []tfsdk.AttributePlanModifier{ planmodifiers.TestWarningDiagModifier{}, planmodifiers.TestWarningDiagModifier{}, - }, nil), - modifyAttributePlanValues{ - config: "TESTDIAG", - plan: "TESTDIAG", - state: "TESTDIAG", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - 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", - ), }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema([]tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, nil), - }, - Private: testEmptyProviderData, }, - }, - "block-warnings-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -868,20 +832,8 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTDIAG", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ + expectedResp: ModifyAttributePlanResponse{ Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), // 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. @@ -890,48 +842,40 @@ func TestBlockModifyPlan(t *testing.T) { "This is a warning", ), }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema([]tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }, nil), + 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": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema([]tfsdk.AttributePlanModifier{ + 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{}, - }, nil), - modifyAttributePlanValues{ - config: "TESTDIAG", - plan: "TESTDIAG", - state: "TESTDIAG", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Error diag", - "This is an error", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema([]tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, nil), }, - Private: testEmptyProviderData, }, - }, - "block-error-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema([]tfsdk.AttributePlanModifier{ @@ -944,61 +888,47 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTDIAG", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ + expectedResp: ModifyAttributePlanResponse{ Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), diag.NewErrorDiagnostic( "Error diag", "This is an error", ), }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema([]tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }, nil), + 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, }, }, "nested-attribute-modified": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }), - modifyAttributePlanValues{ - config: "TESTATTRONE", - plan: "TESTATTRONE", - state: "TESTATTRONE", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("MODIFIED_TWO"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }), + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, }, - Private: testEmptyProviderData, + NestingMode: tfsdk.BlockNestingModeList, }, - }, - "nested-attribute-modified-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1011,58 +941,40 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTATTRONE", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("MODIFIED_TWO"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestAttrPlanValueModifierOne{}, - planmodifiers.TestAttrPlanValueModifierTwo{}, - }), + expectedResp: ModifyAttributePlanResponse{ + 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: "MODIFIED_TWO"}, + }, + }, + }, }, Private: testEmptyProviderData, }, }, "nested-attribute-requires-replacement": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema(nil, []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }), - modifyAttributePlanValues{ - config: "newtestvalue", - plan: "newtestvalue", - state: "testvalue", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }), - }, - RequiresReplace: path.Paths{ - path.Root("test").AtListIndex(0).AtName("nested_attr"), + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, }, - Private: testEmptyProviderData, + NestingMode: tfsdk.BlockNestingModeList, }, - }, - "nested-attribute-requires-replacement-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1074,26 +986,23 @@ func TestBlockModifyPlan(t *testing.T) { state: "testvalue", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - }), + expectedResp: ModifyAttributePlanResponse{ + 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: "newtestvalue"}, + }, + }, + }, }, RequiresReplace: path.Paths{ path.Root("test").AtListIndex(0).AtName("nested_attr"), @@ -1102,6 +1011,19 @@ func TestBlockModifyPlan(t *testing.T) { }, }, "nested-attribute-requires-replacement-passthrough": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1114,14 +1036,23 @@ func TestBlockModifyPlan(t *testing.T) { state: "previousvalue", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTATTRTWO"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestAttrPlanValueModifierOne{}, - }), + expectedResp: ModifyAttributePlanResponse{ + 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: "TESTATTRTWO"}, + }, + }, + }, }, RequiresReplace: path.Paths{ path.Root("test").AtListIndex(0).AtName("nested_attr"), @@ -1130,6 +1061,19 @@ func TestBlockModifyPlan(t *testing.T) { }, }, "nested-attribute-requires-replacement-unset": { + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + }, + NestingMode: tfsdk.BlockNestingModeList, + }, req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1142,53 +1086,41 @@ func TestBlockModifyPlan(t *testing.T) { state: "testvalue", }, ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Plan: tfsdk.Plan{ - Raw: schemaTfValue("newtestvalue"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - resource.RequiresReplace(), - planmodifiers.TestRequiresReplaceFalseModifier{}, - }), + expectedResp: ModifyAttributePlanResponse{ + 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: "newtestvalue"}, + }, + }, + }, }, Private: testEmptyProviderData, }, }, "nested-attribute-warnings": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }), - modifyAttributePlanValues{ - config: "TESTDIAG", - plan: "TESTDIAG", - state: "TESTDIAG", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - 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", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }), + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, }, - Private: testEmptyProviderData, + NestingMode: tfsdk.BlockNestingModeList, }, - }, - "nested-attribute-warnings-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1201,20 +1133,8 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTDIAG", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ + expectedResp: ModifyAttributePlanResponse{ Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), // 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. @@ -1223,48 +1143,40 @@ func TestBlockModifyPlan(t *testing.T) { "This is a warning", ), }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestWarningDiagModifier{}, - planmodifiers.TestWarningDiagModifier{}, - }), + 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, }, }, "nested-attribute-error": { - req: modifyAttributePlanRequest( - path.Root("test"), - schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }), - modifyAttributePlanValues{ - config: "TESTDIAG", - plan: "TESTDIAG", - state: "TESTDIAG", - }, - ), - resp: ModifySchemaPlanResponse{}, - expectedResp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Error diag", - "This is an error", - ), - }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }), + block: tfsdk.Block{ + Attributes: map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, }, - Private: testEmptyProviderData, + NestingMode: tfsdk.BlockNestingModeList, }, - }, - "nested-attribute-error-previous-error": { req: modifyAttributePlanRequest( path.Root("test"), schema(nil, []tfsdk.AttributePlanModifier{ @@ -1277,31 +1189,29 @@ func TestBlockModifyPlan(t *testing.T) { state: "TESTDIAG", }, ), - resp: ModifySchemaPlanResponse{ - Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), - }, - }, - expectedResp: ModifySchemaPlanResponse{ + expectedResp: ModifyAttributePlanResponse{ Diagnostics: diag.Diagnostics{ - diag.NewErrorDiagnostic( - "Previous error diag", - "This was a previous error", - ), diag.NewErrorDiagnostic( "Error diag", "This is an error", ), }, - Plan: tfsdk.Plan{ - Raw: schemaTfValue("TESTDIAG"), - Schema: schema(nil, []tfsdk.AttributePlanModifier{ - planmodifiers.TestErrorDiagModifier{}, - planmodifiers.TestErrorDiagModifier{}, - }), + 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, }, @@ -1313,17 +1223,14 @@ func TestBlockModifyPlan(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - block, ok := tc.req.Config.Schema.Blocks["test"] - - if !ok { - t.Fatalf("Unexpected error getting schema block") + got := ModifyAttributePlanResponse{ + AttributePlan: tc.req.AttributePlan, + Private: tc.req.Private, } - tc.resp.Plan = tc.req.Plan - - BlockModifyPlan(context.Background(), block, tc.req, &tc.resp) + BlockModifyPlan(context.Background(), tc.block, tc.req, &got) - if diff := cmp.Diff(tc.expectedResp, tc.resp, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { + if diff := cmp.Diff(tc.expectedResp, got, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { t.Errorf("Unexpected response (+wanted, -got): %s", diff) } }) diff --git a/internal/fwserver/schema_plan_modification.go b/internal/fwserver/schema_plan_modification.go index 2c8fb9993..d8104eff5 100644 --- a/internal/fwserver/schema_plan_modification.go +++ b/internal/fwserver/schema_plan_modification.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschemadata" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/tfsdk" @@ -57,6 +58,26 @@ type ModifySchemaPlanResponse struct { // package from the tfsdk package and not wanting to export the method. // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/365 func SchemaModifyPlan(ctx context.Context, s fwschema.Schema, req ModifySchemaPlanRequest, resp *ModifySchemaPlanResponse) { + var diags diag.Diagnostics + + configData := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionConfiguration, + Schema: req.Config.Schema, + TerraformValue: req.Config.Raw, + } + + planData := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionPlan, + Schema: req.Plan.Schema, + TerraformValue: req.Plan.Raw, + } + + stateData := &fwschemadata.Data{ + Description: fwschemadata.DataDescriptionState, + Schema: req.State.Schema, + TerraformValue: req.State.Raw, + } + for name, attribute := range s.GetAttributes() { attrReq := tfsdk.ModifyAttributePlanRequest{ AttributePath: path.Root(name), @@ -67,7 +88,51 @@ func SchemaModifyPlan(ctx context.Context, s fwschema.Schema, req ModifySchemaPl Private: req.Private, } - AttributeModifyPlan(ctx, attribute, attrReq, resp) + attrReq.AttributeConfig, diags = configData.ValueAtPath(ctx, attrReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + attrReq.AttributePlan, diags = planData.ValueAtPath(ctx, attrReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + attrReq.AttributeState, diags = stateData.ValueAtPath(ctx, attrReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + attrResp := ModifyAttributePlanResponse{ + AttributePlan: attrReq.AttributePlan, + Private: attrReq.Private, + } + + AttributeModifyPlan(ctx, attribute, attrReq, &attrResp) + + resp.Diagnostics.Append(attrResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, attrReq.AttributePath, attrResp.AttributePlan)...) + + if resp.Diagnostics.HasError() { + return + } + + resp.RequiresReplace = append(resp.RequiresReplace, attrResp.RequiresReplace...) + resp.Private = attrResp.Private } for name, block := range s.GetBlocks() { @@ -80,6 +145,50 @@ func SchemaModifyPlan(ctx context.Context, s fwschema.Schema, req ModifySchemaPl Private: req.Private, } - BlockModifyPlan(ctx, block, blockReq, resp) + blockReq.AttributeConfig, diags = configData.ValueAtPath(ctx, blockReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + blockReq.AttributePlan, diags = planData.ValueAtPath(ctx, blockReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + blockReq.AttributeState, diags = stateData.ValueAtPath(ctx, blockReq.AttributePath) + + resp.Diagnostics.Append(diags...) + + if diags.HasError() { + return + } + + blockResp := ModifyAttributePlanResponse{ + AttributePlan: blockReq.AttributePlan, + Private: blockReq.Private, + } + + BlockModifyPlan(ctx, block, blockReq, &blockResp) + + resp.Diagnostics.Append(blockResp.Diagnostics...) + + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.Plan.SetAttribute(ctx, blockReq.AttributePath, blockResp.AttributePlan)...) + + if resp.Diagnostics.HasError() { + return + } + + resp.RequiresReplace = append(resp.RequiresReplace, blockResp.RequiresReplace...) + resp.Private = blockResp.Private } } diff --git a/internal/fwserver/schema_plan_modification_test.go b/internal/fwserver/schema_plan_modification_test.go new file mode 100644 index 000000000..f9f642848 --- /dev/null +++ b/internal/fwserver/schema_plan_modification_test.go @@ -0,0 +1,2945 @@ +package fwserver + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/planmodifiers" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func TestSchemaModifyPlan(t *testing.T) { + t.Parallel() + + testProviderKeyValue := privatestate.MustMarshalToJson(map[string][]byte{ + "providerKeyOne": []byte(`{"pKeyOne": {"k0": "zero", "k1": 1}}`), + }) + + testProviderData := privatestate.MustProviderData(context.Background(), testProviderKeyValue) + + testEmptyProviderData := privatestate.EmptyProviderData(context.Background()) + + testCases := map[string]struct { + req ModifySchemaPlanRequest + expectedResp ModifySchemaPlanResponse + }{ + "config-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Configuration Read Error", + "An unexpected error was encountered trying to convert an attribute value from the configuration. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + }, + "config-error-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Configuration Read Error", + "An unexpected error was encountered trying to convert an attribute value from the configuration. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + }, + "plan-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Plan Read Error", + "An unexpected error was encountered trying to convert an attribute value from the plan. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + }, + }, + "plan-error-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "Plan Read Error", + "An unexpected error was encountered trying to convert an attribute value from the plan. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + }, + }, + "state-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "State Read Error", + "An unexpected error was encountered trying to convert an attribute value from the state. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + }, + "state-error-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.ListType{ElemType: types.StringType}, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + diag.NewAttributeErrorDiagnostic( + path.Root("test"), + "State Read Error", + "An unexpected error was encountered trying to convert an attribute value from the state. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "Error: can't use tftypes.String<\"testvalue\"> as value of List with ElementType types.primitive, can only use tftypes.String values", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + }, + "no-plan-modifiers": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + }, + }, + "attribute-plan": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "MODIFIED_TWO"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "attribute-request-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-response-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-list-nested-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-set-nested-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-set-nested-usestateforunknown": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, nil), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue1"), + }, + ), + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, nil), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue2"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }), + Required: true, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue1"), + }, + ), + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, tftypes.UnknownValue), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue2"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }), + Required: true, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, "statevalue1"), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue1"), + }, + ), + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, "statevalue2"), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue2"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }), + Required: true, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, "statevalue1"), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue1"), + }, + ), + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_computed": tftypes.String, + "nested_required": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_computed": tftypes.NewValue(tftypes.String, "statevalue2"), + "nested_required": tftypes.NewValue(tftypes.String, "testvalue2"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_computed": { + Type: types.StringType, + Computed: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + resource.UseStateForUnknown(), + }, + }, + "nested_required": { + Type: types.StringType, + Required: true, + }, + }), + Required: true, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "attribute-map-nested-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-single-nested-private": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "testing": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "testing": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "testing": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "testing": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "testing": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanPrivateModifierGet{}, + }, + }, + }), + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanPrivateModifierSet{}, + }, + }, + }, + }, + }, + Private: testProviderData, + }, + }, + "attribute-plan-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "MODIFIED_TWO"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "requires-replacement": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, + "requires-replacement-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "newtestvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, + "requires-replacement-passthrough": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestAttrPlanValueModifierOne{}, + resource.RequiresReplace(), + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRONE"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestAttrPlanValueModifierOne{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTATTRTWO"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + RequiresReplace: path.Paths{ + path.Root("test"), + }, + Private: testEmptyProviderData, + }, + }, + "requires-replacement-unset": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + resource.RequiresReplace(), + planmodifiers.TestRequiresReplaceFalseModifier{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "testvalue"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "warnings": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + 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", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "warnings-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + // 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", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestWarningDiagModifier{}, + planmodifiers.TestWarningDiagModifier{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Error diag", + "This is an error", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + "error-previous-error": { + req: ModifySchemaPlanRequest{ + Config: tfsdk.Config{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + State: tfsdk.State{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + }, + expectedResp: ModifySchemaPlanResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Previous error diag", + "This was a previous error", + ), + diag.NewErrorDiagnostic( + "Error diag", + "This is an error", + ), + }, + Plan: tfsdk.Plan{ + Raw: tftypes.NewValue(tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.String, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue(tftypes.String, "TESTDIAG"), + }), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Type: types.StringType, + Required: true, + PlanModifiers: []tfsdk.AttributePlanModifier{ + planmodifiers.TestErrorDiagModifier{}, + planmodifiers.TestErrorDiagModifier{}, + }, + }, + }, + }, + }, + Private: testEmptyProviderData, + }, + }, + } + + for name, tc := range testCases { + name, tc := name, tc + if name != "attribute-set-nested-usestateforunknown" { + continue + } + + t.Run(name, func(t *testing.T) { + t.Parallel() + + ctx := context.Background() + got := ModifySchemaPlanResponse{ + Plan: tc.req.Plan, + Private: tc.req.Private, + } + + SchemaModifyPlan(ctx, tc.req.Plan.Schema, tc.req, &got) + + if diff := cmp.Diff(tc.expectedResp, got, cmp.AllowUnexported(privatestate.ProviderData{})); diff != "" { + t.Errorf("Unexpected response (-wanted, +got): %s", diff) + } + }) + } +}