Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: Deprecate Attrs, AttrTypes, Elems, ElemTypes, Null, Unknown, and Value fields #502

Merged
merged 3 commits into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 99 additions & 0 deletions .changelog/502.txt
@@ -0,0 +1,99 @@
```release-note:note
types: The `Bool` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `BoolNull()`, `BoolUnknown()`, and `BoolValue()` creation functions and `IsNull()`, `IsUnknown()`, and `ValueBool()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Float64` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `Float64Null()`, `Float64Unknown()`, and `Float64Value()` creation functions and `IsNull()`, `IsUnknown()`, and `ValueFloat64()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Int64` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `Int64Null()`, `Int64Unknown()`, and `Int64Value()` creation functions and `IsNull()`, `IsUnknown()`, and `ValueInt64()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `List` type `Elems`, `ElemType`, `Null`, and `Unknown` fields have been deprecated in preference of the `ListNull()`, `ListUnknown()`, `ListValue()`, and `ListValueMust()` creation functions and `Elements()`, `ElementsAs()`, `ElementType()`, `IsNull()`, and `IsUnknown()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Map` type `Elems`, `ElemType`, `Null`, and `Unknown` fields have been deprecated in preference of the `MapNull()`, `MapUnknown()`, `MapValue()`, and `MapValueMust()` creation functions and `Elements()`, `ElementsAs()`, `ElementType()`, `IsNull()`, and `IsUnknown()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Number` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `NumberNull()`, `NumberUnknown()`, and `NumberValue()` creation functions and `IsNull()`, `IsUnknown()`, and `ValueBigFloat()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Object` type `Attrs`, `AttrTypes`, `Null`, and `Unknown` fields have been deprecated in preference of the `ObjectNull()`, `ObjectUnknown()`, `ObjectValue()`, and `ObjectValueMust()` creation functions and `As()`, `Attributes()`, `AttributeTypes()`, `IsNull()`, and `IsUnknown()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `Set` type `Elems`, `ElemType`, `Null`, and `Unknown` fields have been deprecated in preference of the `SetNull()`, `SetUnknown()`, `SetValue()`, and `SetValueMust()` creation functions and `Elements()`, `ElementsAs()`, `ElementType()`, `IsNull()`, and `IsUnknown()` methods. The fields will be removed in a future release.
```

```release-note:note
types: The `String` type `Null`, `Unknown`, and `Value` fields have been deprecated in preference of the `StringNull()`, `StringUnknown()`, and `StringValue()` creation functions and `IsNull()`, `IsUnknown()`, and `ValueString()` methods. The fields will be removed in a future release.
```

```release-note:enhancement
types: Added `BoolNull()`, `BoolUnknown()`, and `BoolValue()` functions, which create immutable `Bool` values
```

```release-note:enhancement
types: Added `Float64Null()`, `Float64Unknown()`, and `Float64Value()` functions, which create immutable `Float64` values
```

```release-note:enhancement
types: Added `Int64Null()`, `Int64Unknown()`, and `Int64Value()` functions, which create immutable `Int64` values
```

```release-note:enhancement
types: Added `ListNull()`, `ListUnknown()`, `ListValue()`, and `ListValueMust()` functions, which create immutable `List` values
```

```release-note:enhancement
types: Added `MapNull()`, `MapUnknown()`, `MapValue()`, and `MapValueMust()` functions, which create immutable `Map` values
```

```release-note:enhancement
types: Added `NumberNull()`, `NumberUnknown()`, and `NumberValue()` functions, which create immutable `Number` values
```

```release-note:enhancement
types: Added `SetNull()`, `SetUnknown()`, `SetValue()`, and `SetValueMust()` functions, which create immutable `Set` values
```

```release-note:enhancement
types: Added `StringNull()`, `StringUnknown()`, and `StringValue()` functions, which create immutable `String` values
```

```release-note:enhancement
types: Added `Bool` type `ValueBool()` method, which returns the `bool` of the known value or `false` if null or unknown
```

```release-note:enhancement
types: Added `Float64` type `ValueFloat64()` method, which returns the `float64` of the known value or `0.0` if null or unknown
```

```release-note:enhancement
types: Added `Int64` type `ValueInt64()` method, which returns the `int64` of the known value or `0` if null or unknown
```

```release-note:enhancement
types: Added `List` type `Elements()` method, which returns the `[]attr.Value` of the known values or `nil` if null or unknown
```

```release-note:enhancement
types: Added `Map` type `Elements()` method, which returns the `map[string]attr.Value` of the known values or `nil` if null or unknown
```

```release-note:enhancement
types: Added `Number` type `ValueBigFloat()` method, which returns the `*big.Float` of the known value or `nil` if null or unknown
```

```release-note:enhancement
types: Added `Set` type `Elements()` method, which returns the `[]attr.Value` of the known values or `nil` if null or unknown
```

```release-note:enhancement
types: Added `String` type `ValueString()` method, which returns the `string` of the known value or `""` if null or unknown
```
23 changes: 13 additions & 10 deletions internal/fwserver/attr_value.go
Expand Up @@ -68,15 +68,16 @@ func listElemObject(ctx context.Context, schemaPath path.Path, list types.List,
return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, tftypes.UnknownValue)
}

if index >= len(list.Elems) {
if index >= len(list.Elements()) {
return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, nil)
}

return coerceObjectValue(schemaPath, list.Elems[index])
return coerceObjectValue(schemaPath, list.Elements()[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))
elemType := list.ElementType(ctx)
elemValue, err := elemType.ValueFromTerraform(ctx, tftypes.NewValue(elemType.TerraformType(ctx), tfValue))

if err != nil {
return types.Object{Null: true}, diag.Diagnostics{
Expand All @@ -96,7 +97,7 @@ func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key s
return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, tftypes.UnknownValue)
}

elemValue, ok := m.Elems[key]
elemValue, ok := m.Elements()[key]

if !ok {
return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, nil)
Expand All @@ -106,7 +107,8 @@ func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key s
}

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))
elemType := m.ElementType(ctx)
elemValue, err := elemType.ValueFromTerraform(ctx, tftypes.NewValue(elemType.TerraformType(ctx), tfValue))

if err != nil {
return types.Object{Null: true}, diag.Diagnostics{
Expand All @@ -128,13 +130,13 @@ func objectAttributeValue(ctx context.Context, object types.Object, attributeNam

// A panic here indicates a bug somewhere else in the framework or an
// invalid test case.
return object.Attrs[attributeName], nil
return object.Attributes()[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]
attrType := object.AttributeTypes(ctx)[attributeName]

elemValue, err := attrType.ValueFromTerraform(ctx, tftypes.NewValue(attrType.TerraformType(ctx), tfValue))

Expand All @@ -156,15 +158,16 @@ func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, ind
return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, tftypes.UnknownValue)
}

if index >= len(set.Elems) {
if index >= len(set.Elements()) {
return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, nil)
}

return coerceObjectValue(schemaPath, set.Elems[index])
return coerceObjectValue(schemaPath, set.Elements()[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))
elemType := set.ElementType(ctx)
elemValue, err := elemType.ValueFromTerraform(ctx, tftypes.NewValue(elemType.TerraformType(ctx), tfValue))

if err != nil {
return types.Object{Null: true}, diag.Diagnostics{
Expand Down
92 changes: 77 additions & 15 deletions internal/fwserver/attribute_plan_modification.go
Expand Up @@ -13,6 +13,7 @@ import (
"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 {
Expand Down Expand Up @@ -85,6 +86,11 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

// Null and unknown values should not have nested schema to modify.
if req.AttributePlan.IsNull() || req.AttributePlan.IsUnknown() {
return
}

if a.GetAttributes() == nil || len(a.GetAttributes().GetAttributes()) == 0 {
return
}
Expand Down Expand Up @@ -116,7 +122,9 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

for idx, planElem := range planList.Elems {
planElements := planList.Elements()

for idx, planElem := range planElements {
attrPath := req.AttributePath.AtListIndex(idx)

configObject, diags := listElemObject(ctx, attrPath, configList, idx, fwschemadata.DataDescriptionConfiguration)
Expand All @@ -143,6 +151,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

planAttributes := planObject.Attributes()

for name, attr := range a.GetAttributes().GetAttributes() {
attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration)

Expand Down Expand Up @@ -187,16 +197,28 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo

AttributeModifyPlan(ctx, attr, attrReq, &attrResp)

planObject.Attrs[name] = attrResp.AttributePlan
planAttributes[name] = attrResp.AttributePlan
resp.Diagnostics.Append(attrResp.Diagnostics...)
resp.RequiresReplace = attrResp.RequiresReplace
resp.Private = attrResp.Private
}

planList.Elems[idx] = planObject
planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
}

resp.AttributePlan = planList
resp.AttributePlan, diags = types.ListValue(planList.ElementType(ctx), planElements)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
case fwschema.NestingModeSet:
configSet, diags := coerceSetValue(req.AttributePath, req.AttributeConfig)

Expand All @@ -222,7 +244,9 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

for idx, planElem := range planSet.Elems {
planElements := planSet.Elements()

for idx, planElem := range planElements {
attrPath := req.AttributePath.AtSetValue(planElem)

configObject, diags := setElemObject(ctx, attrPath, configSet, idx, fwschemadata.DataDescriptionConfiguration)
Expand All @@ -249,6 +273,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

planAttributes := planObject.Attributes()

for name, attr := range a.GetAttributes().GetAttributes() {
attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration)

Expand Down Expand Up @@ -293,16 +319,28 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo

AttributeModifyPlan(ctx, attr, attrReq, &attrResp)

planObject.Attrs[name] = attrResp.AttributePlan
planAttributes[name] = attrResp.AttributePlan
resp.Diagnostics.Append(attrResp.Diagnostics...)
resp.RequiresReplace = attrResp.RequiresReplace
resp.Private = attrResp.Private
}

planSet.Elems[idx] = planObject
planElements[idx], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
}

resp.AttributePlan = planSet
resp.AttributePlan, diags = types.SetValue(planSet.ElementType(ctx), planElements)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
case fwschema.NestingModeMap:
configMap, diags := coerceMapValue(req.AttributePath, req.AttributeConfig)

Expand All @@ -328,7 +366,9 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

for key, planElem := range planMap.Elems {
planElements := planMap.Elements()

for key, planElem := range planElements {
attrPath := req.AttributePath.AtMapKey(key)

configObject, diags := mapElemObject(ctx, attrPath, configMap, key, fwschemadata.DataDescriptionConfiguration)
Expand All @@ -355,6 +395,8 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

planAttributes := planObject.Attributes()

for name, attr := range a.GetAttributes().GetAttributes() {
attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration)

Expand Down Expand Up @@ -399,16 +441,28 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo

AttributeModifyPlan(ctx, attr, attrReq, &attrResp)

planObject.Attrs[name] = attrResp.AttributePlan
planAttributes[name] = attrResp.AttributePlan
resp.Diagnostics.Append(attrResp.Diagnostics...)
resp.RequiresReplace = attrResp.RequiresReplace
resp.Private = attrResp.Private
}

planMap.Elems[key] = planObject
planElements[key], diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
}

resp.AttributePlan = planMap
resp.AttributePlan, diags = types.MapValue(planMap.ElementType(ctx), planElements)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
case fwschema.NestingModeSingle:
configObject, diags := coerceObjectValue(req.AttributePath, req.AttributeConfig)

Expand All @@ -434,10 +488,12 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo
return
}

if len(planObject.Attrs) == 0 {
if len(planObject.Attributes()) == 0 {
return
}

planAttributes := planObject.Attributes()

for name, attr := range a.GetAttributes().GetAttributes() {
attrConfig, diags := objectAttributeValue(ctx, configObject, name, fwschemadata.DataDescriptionConfiguration)

Expand Down Expand Up @@ -482,13 +538,19 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo

AttributeModifyPlan(ctx, attr, attrReq, &attrResp)

planObject.Attrs[name] = attrResp.AttributePlan
planAttributes[name] = attrResp.AttributePlan
resp.Diagnostics.Append(attrResp.Diagnostics...)
resp.RequiresReplace = attrResp.RequiresReplace
resp.Private = attrResp.Private
}

resp.AttributePlan = planObject
resp.AttributePlan, diags = types.ObjectValue(planObject.AttributeTypes(ctx), planAttributes)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}
default:
err := fmt.Errorf("unknown attribute nesting mode (%T: %v) at path: %s", nm, nm, req.AttributePath)
resp.Diagnostics.AddAttributeError(
Expand Down