From 0eb939e792c2fb718b8f9bbeed39cdbfde70fe81 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 10 Nov 2022 10:35:09 -0500 Subject: [PATCH] tfsdk: Document and clarify GetAttribute and SetAttribute further (#534) Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/66 Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/186 Documenting existing intentions and behaviors with `tfsdk.Config`, `tfsdk.Plan`, and `tfsdk.State` type `GetAttribute()` and `SetAttribute()` methods. --- tfsdk/config.go | 9 +++++++-- tfsdk/plan.go | 13 +++++++++++-- tfsdk/state.go | 13 +++++++++++-- website/docs/plugin/framework/accessing-values.mdx | 4 ++-- website/docs/plugin/framework/writing-state.mdx | 6 ++++-- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/tfsdk/config.go b/tfsdk/config.go index 8636fa2c7..3e6382c94 100644 --- a/tfsdk/config.go +++ b/tfsdk/config.go @@ -20,8 +20,13 @@ func (c Config) Get(ctx context.Context, target interface{}) diag.Diagnostics { return c.data().Get(ctx, target) } -// GetAttribute retrieves the attribute found at `path` and populates the -// `target` with the value. +// GetAttribute retrieves the attribute or block found at `path` and populates +// the `target` with the value. This method is intended for top level schema +// attributes or blocks. Use `types` package methods or custom types to step +// into collections. +// +// Attributes or elements under null or unknown collections return null +// values, however this behavior is not protected by compatibility promises. func (c Config) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { return c.data().GetAtPath(ctx, path, target) } diff --git a/tfsdk/plan.go b/tfsdk/plan.go index 7b94536c9..264765be8 100644 --- a/tfsdk/plan.go +++ b/tfsdk/plan.go @@ -20,8 +20,13 @@ func (p Plan) Get(ctx context.Context, target interface{}) diag.Diagnostics { return p.data().Get(ctx, target) } -// GetAttribute retrieves the attribute found at `path` and populates the -// `target` with the value. +// GetAttribute retrieves the attribute or block found at `path` and populates +// the `target` with the value. This method is intended for top level schema +// attributes or blocks. Use `types` package methods or custom types to step +// into collections. +// +// Attributes or elements under null or unknown collections return null +// values, however this behavior is not protected by compatibility promises. func (p Plan) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { return p.data().GetAtPath(ctx, path, target) } @@ -58,6 +63,10 @@ func (p *Plan) Set(ctx context.Context, val interface{}) diag.Diagnostics { // path does not have a value, it will be added, including any parent attribute // paths as necessary. // +// The value must not be an untyped nil. Use a typed nil or types package null +// value function instead. For example with a types.StringType attribute, +// use (*string)(nil) or types.StringNull(). +// // Lists can only have the next element added according to the current length. func (p *Plan) SetAttribute(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics { data := p.data() diff --git a/tfsdk/state.go b/tfsdk/state.go index 40c369f4e..31939e109 100644 --- a/tfsdk/state.go +++ b/tfsdk/state.go @@ -21,8 +21,13 @@ func (s State) Get(ctx context.Context, target interface{}) diag.Diagnostics { return s.data().Get(ctx, target) } -// GetAttribute retrieves the attribute found at `path` and populates the -// `target` with the value. +// GetAttribute retrieves the attribute or block found at `path` and populates +// the `target` with the value. This method is intended for top level schema +// attributes or blocks. Use `types` package methods or custom types to step +// into collections. +// +// Attributes or elements under null or unknown collections return null +// values, however this behavior is not protected by compatibility promises. func (s State) GetAttribute(ctx context.Context, path path.Path, target interface{}) diag.Diagnostics { return s.data().GetAtPath(ctx, path, target) } @@ -69,6 +74,10 @@ func (s *State) Set(ctx context.Context, val interface{}) diag.Diagnostics { // path does not have a value, it will be added, including any parent attribute // paths as necessary. // +// The value must not be an untyped nil. Use a typed nil or types package null +// value function instead. For example with a types.StringType attribute, +// use (*string)(nil) or types.StringNull(). +// // Lists can only have the next element added according to the current length. func (s *State) SetAttribute(ctx context.Context, path path.Path, val interface{}) diag.Diagnostics { data := s.data() diff --git a/website/docs/plugin/framework/accessing-values.mdx b/website/docs/plugin/framework/accessing-values.mdx index f23048014..44f71aec9 100644 --- a/website/docs/plugin/framework/accessing-values.mdx +++ b/website/docs/plugin/framework/accessing-values.mdx @@ -67,9 +67,9 @@ explanation on how objects can be converted into Go types. To descend into deeper nested data structures, the `types.List`, `types.Map`, and `types.Set` types each have an `ElementsAs()` method. The `types.Object` type has an `As()` method. -## Get a Single Attribute's Value +## Get a Single Attribute or Block Value -Use the `GetAttribute` method to retrieve a single attribute value from the configuration, plan, and state. +Use the `GetAttribute` method to retrieve a top level attribute or block value from the configuration, plan, and state. ```go func (r ThingResource) Read(ctx context.Context, diff --git a/website/docs/plugin/framework/writing-state.mdx b/website/docs/plugin/framework/writing-state.mdx index 613018fea..5ee5ca4ce 100644 --- a/website/docs/plugin/framework/writing-state.mdx +++ b/website/docs/plugin/framework/writing-state.mdx @@ -64,9 +64,11 @@ The state information is represented as an object, and gets persisted like an object. Refer to the [conversion rules](#conversion-rules) for an explanation on how objects get persisted and what Go types are valid for persisting as an object. -## Set a Single Attribute's Value +## Set a Single Attribute or Block Value -Use the `SetAttribute` method to set an individual attribute value. +Use the `SetAttribute` method to set an individual attribute or block value. + +-> The value must not be an untyped `nil`. Use a typed `nil` or `types` package null value function instead. For example with a `types.StringType` attribute, use `(*string)(nil)` or `types.StringNull()`. ```go func (r ThingResource) Read(ctx context.Context,