diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 1165982b4..bfdea12d0 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -3,12 +3,13 @@ package fwserver import ( "context" + "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/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) { @@ -35,16 +36,16 @@ func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Dia return m, nil } -func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { - object, ok := value.(types.Object) +func coerceObjectValue(schemaPath path.Path, value attr.Value) (attr.Value, diag.Diagnostics) { + object, ok := value.Type(context.Background()).(attr.TypeWithAttributeTypes) if !ok { - return types.Object{Null: true}, diag.Diagnostics{ + return object.ValueType(context.Background()), diag.Diagnostics{ attributePlanModificationWalkError(schemaPath, value), } } - return object, nil + return value, nil } func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { @@ -59,7 +60,7 @@ func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Dia return set, nil } -func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { +func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { if list.IsNull() { return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, nil) } @@ -75,7 +76,7 @@ func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, 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) { +func listElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, list types.List, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { elemValue, err := list.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(list.ElemType.TerraformType(ctx), tfValue)) if err != nil { @@ -87,7 +88,7 @@ func listElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, return coerceObjectValue(schemaPath, elemValue) } -func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key string, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { +func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key string, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { if m.IsNull() { return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, nil) } @@ -105,7 +106,7 @@ func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key s return coerceObjectValue(schemaPath, elemValue) } -func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, m types.Map, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { +func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, m types.Map, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { elemValue, err := m.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(m.ElemType.TerraformType(ctx), tfValue)) if err != nil { @@ -117,7 +118,7 @@ func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, return coerceObjectValue(schemaPath, elemValue) } -func objectAttributeValue(ctx context.Context, object types.Object, attributeName string, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { +func objectAttributeValue(ctx context.Context, object attr.Value, attributeName string, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { if object.IsNull() { return objectAttributeValueFromTerraformValue(ctx, object, attributeName, description, nil) } @@ -126,15 +127,19 @@ func objectAttributeValue(ctx context.Context, object types.Object, attributeNam return objectAttributeValueFromTerraformValue(ctx, object, attributeName, description, tftypes.UnknownValue) } + attrTypes := object.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + attrType := attrTypes[attributeName] + // A panic here indicates a bug somewhere else in the framework or an // invalid test case. - return object.Attrs[attributeName], nil + return attrType.ValueType(ctx), nil } -func objectAttributeValueFromTerraformValue(ctx context.Context, object types.Object, attributeName string, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { +func objectAttributeValueFromTerraformValue(ctx context.Context, object attr.Value, 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] + attrTypes := object.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + attrType := attrTypes[attributeName] elemValue, err := attrType.ValueFromTerraform(ctx, tftypes.NewValue(attrType.TerraformType(ctx), tfValue)) @@ -147,7 +152,7 @@ func objectAttributeValueFromTerraformValue(ctx context.Context, object types.Ob return elemValue, nil } -func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { +func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, index int, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { if set.IsNull() { return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, nil) } @@ -163,7 +168,7 @@ func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, ind 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) { +func setElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, set types.Set, description fwschemadata.DataDescription, tfValue any) (attr.Value, diag.Diagnostics) { elemValue, err := set.ElemType.ValueFromTerraform(ctx, tftypes.NewValue(set.ElemType.TerraformType(ctx), tfValue)) if err != nil { diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index 32281b42c..b4f6981b0 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -143,6 +143,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -187,7 +189,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -249,6 +251,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -293,7 +297,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -355,6 +359,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range a.GetAttributes().GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -399,7 +405,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -434,7 +440,9 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - if len(planObject.Attrs) == 0 { + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + + if len(planObjectAttrs) == 0 { return } @@ -482,7 +490,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index 747f8f39b..44b85c233 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -109,6 +109,8 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -153,7 +155,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -203,7 +205,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObject.Attrs[name] = blockResp.AttributePlan + planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private @@ -265,6 +267,8 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -309,7 +313,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -359,7 +363,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObject.Attrs[name] = blockResp.AttributePlan + planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private @@ -394,10 +398,12 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - if planObject.Attrs == nil { - planObject.Attrs = make(map[string]attr.Value) + if planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() == nil { + planObject.Type(ctx).(attr.TypeWithAttributeTypes).WithAttributeTypes(make(map[string]attr.Type)) } + planObjectAttrs := planObject.Type(ctx).(attr.TypeWithAttributeTypes).AttributeTypes() + for name, attr := range b.GetAttributes() { attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration) @@ -442,7 +448,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr AttributeModifyPlan(ctx, attr, attrReq, &attrResp) - planObject.Attrs[name] = attrResp.AttributePlan + planObjectAttrs[name] = attrResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(attrResp.Diagnostics...) resp.RequiresReplace = attrResp.RequiresReplace resp.Private = attrResp.Private @@ -492,7 +498,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr BlockModifyPlan(ctx, block, blockReq, &blockResp) - planObject.Attrs[name] = blockResp.AttributePlan + planObjectAttrs[name] = blockResp.AttributePlan.Type(ctx) resp.Diagnostics.Append(blockResp.Diagnostics...) resp.RequiresReplace = blockResp.RequiresReplace resp.Private = blockResp.Private diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index 95c9c32ec..32a9d88aa 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "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" @@ -185,8 +186,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), len(s.Elems))) } case fwschema.BlockNestingModeSingle: - s, ok := req.AttributeConfig.(types.Object) - + _, ok := req.AttributeConfig.Type(ctx).(attr.TypeWithAttributeTypes) 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( @@ -228,7 +228,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics = nestedAttrResp.Diagnostics } - if b.GetMinItems() == 1 && s.IsNull() { + if b.GetMinItems() == 1 && req.AttributeConfig.IsNull() { resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), 0)) } default: diff --git a/tfsdk/block.go b/tfsdk/block.go index 187a01052..415cee0a4 100644 --- a/tfsdk/block.go +++ b/tfsdk/block.go @@ -4,10 +4,11 @@ import ( "fmt" "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/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var _ tftypes.AttributePathStepper = Block{} @@ -24,6 +25,8 @@ var _ fwschema.Block = Block{} // The NestingMode field must be set or a runtime error will be raised by the // framework when fetching the schema. type Block struct { + Typ attr.TypeWithAttributeTypes + // Attributes are value fields inside the block. This map of attributes // behaves exactly like the map of attributes on the Schema type. Attributes map[string]Attribute @@ -210,18 +213,25 @@ func (b Block) GetValidators() []AttributeValidator { // attributeType returns an attr.Type corresponding to the block. func (b Block) Type() attr.Type { - attrType := types.ObjectType{ - AttrTypes: map[string]attr.Type{}, + var attrType attr.TypeWithAttributeTypes + attrType = types.ObjectType{} + + if b.Typ != nil { + attrType = b.Typ } + attrTypes := make(map[string]attr.Type, len(b.Attributes)+len(b.Blocks)) + for attrName, attr := range b.Attributes { - attrType.AttrTypes[attrName] = attr.FrameworkType() + attrTypes[attrName] = attr.FrameworkType() } for blockName, block := range b.Blocks { - attrType.AttrTypes[blockName] = block.Type() + attrTypes[blockName] = block.Type() } + attrType = attrType.WithAttributeTypes(attrTypes) + switch b.NestingMode { case BlockNestingModeList: return types.ListType{