hashicorp / hashicorp/terraform-plugin-framework

Consider Object Nested Attribute Default Implementation

Open
#777 4 comments 2 reactions 0 assignees View on GitHub
enhancement
Dominant language
Go
Stars
384
Forks
107
Avg merge
3m
Merged PRs (30d)
1

Description

### Module version

```
v1.3.1
```

### Use-cases

When working with nested attributes, it may be desirable to automatically have an object default based on all the nested attribute default values for when the object is null. Today, this is a manual process to re-create the object default value based on the nested attribute defaults.

```go
schema.SingleNestedAttribute{
// ... other fields ...
Attributes: map[string]schema.Attribute{
"attribute_one": schema.StringAttribute{
// ... other fields ...
Default: stringdefault.StaticString("one"),
}
"attribute_two": schema.StringAttribute{
// ... other fields ...
Default: stringdefault.StaticString("two"),
}
},
Default: objectdefault.StaticValue(
map[string]attr.Type{
"attribute_one": types.StringType,
"attribute_two": types.StringType,
},
map[string]attr.Value{
"attribute_one": types.StringValue("one"),
"attribute_two": types.StringValue("two"),
},
)
}
```

### Proposal

_NOTE: `NestedAttributes` is not possible at the moment due to import cycle._

Add an `AttributeTypes` field and `NestedAttributes` field to `defaults.ObjectRequest`:

```go
type ObjectRequest struct {
// AttributeTypes returns the mapping of nested attribute
// names to attribute types.
AttributeTypes map[string]attr.Type

// NestedAttributes is populated with the schema's nested
// attributes for this object, if the object itself was a
// NestedAttributeObject.
NestedAttributes map[string]fwschema.Attribute

// Path contains the path of the attribute for setting the
// default value. Use this path for any response diagnostics.
Path path.Path
}
```

In `internal/fwschemadata`, update the object handling to something like:

```go
case fwschema.AttributeWithObjectDefaultValue:
defaultValue := a.ObjectDefaultValue()

if defaultValue != nil {
req := defaults.ObjectRequest{
AttributeTypes: a.GetType(), // real implementation is more complicated
Path: fwPath,
}

if na, ok := a.(fwschema.NestedAttribute); ok {
req.NestedAttributes = na.GetNestedObject().GetAttributes()
}

resp := defaults.ObjectResponse{}

defaultValue.DefaultObject(ctx, req, &resp)

logging.FrameworkTrace(ctx, fmt.Sprintf("setting attribute %s to default value: %s", fwPath, resp.PlanValue))

return resp.PlanValue.ToTerraformValue(ctx)
}
```

Create a new `resource/schema/objectdefault` implementation for instantiating the object using all the nested attribute defaults, e.g.

```go
func NestedAttributeDefaults() defaults.Object {/* ... */}
```

It may also be worth considering whether there should be a simplified object value default function that only takes in the `map[string]attr.Value`, since we can theoretically have the `map[string]attr.Type` already available.

### References

- https://github.com/hashicorp/terraform-plugin-framework/issues/726#issuecomment-1593693448

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.