Skip to content

Commit e3cf75a

Browse files
authored
add map, list and single nested blocks and attributes (#1201)
1 parent ba065ff commit e3cf75a

27 files changed

+5896
-12
lines changed

list/schema/attribute.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ import (
1717
// - ListAttribute
1818
// - MapAttribute
1919
// - NumberAttribute
20+
// - ObjectAttribute
2021
// - StringAttribute
2122
//
23+
// Additionally, the NestedAttribute interface extends Attribute with nested
24+
// attributes. Only supported in protocol version 6. Implementations in this
25+
// package include:
26+
// - ListNestedAttribute
27+
// - MapNestedAttribute
28+
// - SingleNestedAttribute
29+
//
2230
// In practitioner configurations, an equals sign (=) is required to set
2331
// the value. [Configuration Reference]
2432
//

list/schema/bool_attribute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = BoolAttribute{}
2020
_ fwxschema.AttributeWithBoolValidators = BoolAttribute{}

list/schema/dynamic_attribute.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = DynamicAttribute{}
2020
_ fwxschema.AttributeWithDynamicValidators = DynamicAttribute{}
@@ -163,8 +163,7 @@ func (a DynamicAttribute) IsSensitive() bool {
163163
return false
164164
}
165165

166-
// IsWriteOnly returns false as write-only attributes are not relevant to ephemeral resource schemas,
167-
// as these schemas describe data that is explicitly not saved to any artifact.
166+
// IsWriteOnly returns false because it does not apply to ListResource schemas.
168167
func (a DynamicAttribute) IsWriteOnly() bool {
169168
return false
170169
}

list/schema/float32_attribute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = Float32Attribute{}
2020
_ fwxschema.AttributeWithFloat32Validators = Float32Attribute{}

list/schema/float64_attribute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = Float64Attribute{}
2020
_ fwxschema.AttributeWithFloat64Validators = Float64Attribute{}

list/schema/int32_attribute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = Int32Attribute{}
2020
_ fwxschema.AttributeWithInt32Validators = Int32Attribute{}

list/schema/int64_attribute.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1515
)
1616

17-
// Ensure the implementation satisifies the desired interfaces.
17+
// Ensure the implementation satisfies the desired interfaces.
1818
var (
1919
_ Attribute = Int64Attribute{}
2020
_ fwxschema.AttributeWithInt64Validators = Int64Attribute{}
@@ -170,7 +170,7 @@ func (a Int64Attribute) IsRequired() bool {
170170
return a.Required
171171
}
172172

173-
// IsSensitive returns false because it does not apply to ephemeral resource schemas.
173+
// IsSensitive returns false because it does not apply to ListResource schemas.
174174
func (a Int64Attribute) IsSensitive() bool {
175175
return false
176176
}

list/schema/list_attribute.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
1818
)
1919

20-
// Ensure the implementation satisifies the desired interfaces.
20+
// Ensure the implementation satisfies the desired interfaces.
2121
var (
2222
_ Attribute = ListAttribute{}
2323
_ fwschema.AttributeWithValidateImplementation = ListAttribute{}
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package schema
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
12+
"github.com/hashicorp/terraform-plugin-framework/attr"
13+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
14+
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
15+
"github.com/hashicorp/terraform-plugin-framework/internal/fwtype"
16+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
17+
"github.com/hashicorp/terraform-plugin-framework/types"
18+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
19+
)
20+
21+
// Ensure the implementation satisfies the desired interfaces.
22+
var (
23+
_ NestedAttribute = ListNestedAttribute{}
24+
_ fwschema.AttributeWithValidateImplementation = ListNestedAttribute{}
25+
_ fwxschema.AttributeWithListValidators = ListNestedAttribute{}
26+
)
27+
28+
// ListNestedAttribute represents an attribute that is a list of objects where
29+
// the object attributes can be fully defined, including further nested
30+
// attributes. When retrieving the value for this attribute, use types.List
31+
// as the value type unless the CustomType field is set. The NestedObject field
32+
// must be set. Nested attributes are only compatible with protocol version 6.
33+
//
34+
// Use ListAttribute if the underlying elements are of a single type and do
35+
// not require definition beyond type information.
36+
//
37+
// Terraform configurations configure this attribute using expressions that
38+
// return a list of objects or directly via square and curly brace syntax.
39+
//
40+
// # list of objects
41+
// example_attribute = [
42+
// {
43+
// nested_attribute = #...
44+
// },
45+
// ]
46+
//
47+
// Terraform configurations reference this attribute using expressions that
48+
// accept a list of objects or an element directly via square brace 0-based
49+
// index syntax:
50+
//
51+
// # first known object
52+
// .example_attribute[0]
53+
// # first known object nested_attribute value
54+
// .example_attribute[0].nested_attribute
55+
type ListNestedAttribute struct {
56+
// NestedObject is the underlying object that contains nested attributes.
57+
// This field must be set.
58+
//
59+
// Nested attributes that contain a dynamic type (i.e. DynamicAttribute) are not supported.
60+
// If underlying dynamic values are required, replace this attribute definition with
61+
// DynamicAttribute instead.
62+
NestedObject NestedAttributeObject
63+
64+
// CustomType enables the use of a custom attribute type in place of the
65+
// default types.ListType of types.ObjectType. When retrieving data, the
66+
// basetypes.ListValuable associated with this custom type must be used in
67+
// place of types.List.
68+
CustomType basetypes.ListTypable
69+
70+
// Required indicates whether the practitioner must enter a value for
71+
// this attribute or not. Required and Optional cannot both be true.
72+
Required bool
73+
74+
// Optional indicates whether the practitioner can choose to enter a value
75+
// for this attribute or not. Optional and Required cannot both be true.
76+
Optional bool
77+
78+
// Description is used in various tooling, like the language server, to
79+
// give practitioners more information about what this attribute is,
80+
// what it's for, and how it should be used. It should be written as
81+
// plain text, with no special formatting.
82+
Description string
83+
84+
// MarkdownDescription is used in various tooling, like the
85+
// documentation generator, to give practitioners more information
86+
// about what this attribute is, what it's for, and how it should be
87+
// used. It should be formatted using Markdown.
88+
MarkdownDescription string
89+
90+
// DeprecationMessage defines warning diagnostic details to display when
91+
// practitioner configurations use this Attribute. The warning diagnostic
92+
// summary is automatically set to "Attribute Deprecated" along with
93+
// configuration source file and line information.
94+
//
95+
// Set this field to a practitioner actionable message such as:
96+
//
97+
// - "Configure other_attribute instead. This attribute will be removed
98+
// in the next major version of the provider."
99+
// - "Remove this attribute's configuration as it no longer is used and
100+
// the attribute will be removed in the next major version of the
101+
// provider."
102+
//
103+
// In Terraform 1.2.7 and later, this warning diagnostic is displayed any
104+
// time a practitioner attempts to configure a value for this attribute and
105+
// certain scenarios where this attribute is referenced.
106+
//
107+
// In Terraform 1.2.6 and earlier, this warning diagnostic is only
108+
// displayed when the Attribute is Required or Optional, and if the
109+
// practitioner configuration sets the value to a known or unknown value
110+
// (which may eventually be null). It has no effect when the Attribute is
111+
// Computed-only (read-only; not Required or Optional).
112+
//
113+
// Across any Terraform version, there are no warnings raised for
114+
// practitioner configuration values set directly to null, as there is no
115+
// way for the framework to differentiate between an unset and null
116+
// configuration due to how Terraform sends configuration information
117+
// across the protocol.
118+
//
119+
// Additional information about deprecation enhancements for read-only
120+
// attributes can be found in:
121+
//
122+
// - https://github.com/hashicorp/terraform/issues/7569
123+
//
124+
DeprecationMessage string
125+
126+
// Validators define value validation functionality for the attribute. All
127+
// elements of the slice of AttributeValidator are run, regardless of any
128+
// previous error diagnostics.
129+
//
130+
// Many common use case validators can be found in the
131+
// github.com/hashicorp/terraform-plugin-framework-validators Go module.
132+
//
133+
// If the Type field points to a custom type that implements the
134+
// xattr.TypeWithValidate interface, the validators defined in this field
135+
// are run in addition to the validation defined by the type.
136+
Validators []validator.List
137+
}
138+
139+
// ApplyTerraform5AttributePathStep returns the Attributes field value if step
140+
// is ElementKeyInt, otherwise returns an error.
141+
func (a ListNestedAttribute) ApplyTerraform5AttributePathStep(step tftypes.AttributePathStep) (interface{}, error) {
142+
_, ok := step.(tftypes.ElementKeyInt)
143+
144+
if !ok {
145+
return nil, fmt.Errorf("cannot apply step %T to ListNestedAttribute", step)
146+
}
147+
148+
return a.NestedObject, nil
149+
}
150+
151+
// Equal returns true if the given Attribute is a ListNestedAttribute
152+
// and all fields are equal.
153+
func (a ListNestedAttribute) Equal(o fwschema.Attribute) bool {
154+
other, ok := o.(ListNestedAttribute)
155+
156+
if !ok {
157+
return false
158+
}
159+
160+
return fwschema.NestedAttributesEqual(a, other)
161+
}
162+
163+
// GetDeprecationMessage returns the DeprecationMessage field value.
164+
func (a ListNestedAttribute) GetDeprecationMessage() string {
165+
return a.DeprecationMessage
166+
}
167+
168+
// GetDescription returns the Description field value.
169+
func (a ListNestedAttribute) GetDescription() string {
170+
return a.Description
171+
}
172+
173+
// GetMarkdownDescription returns the MarkdownDescription field value.
174+
func (a ListNestedAttribute) GetMarkdownDescription() string {
175+
return a.MarkdownDescription
176+
}
177+
178+
// GetNestedObject returns the NestedObject field value.
179+
func (a ListNestedAttribute) GetNestedObject() fwschema.NestedAttributeObject {
180+
return a.NestedObject
181+
}
182+
183+
// GetNestingMode always returns NestingModeList.
184+
func (a ListNestedAttribute) GetNestingMode() fwschema.NestingMode {
185+
return fwschema.NestingModeList
186+
}
187+
188+
// GetType returns ListType of ObjectType or CustomType.
189+
func (a ListNestedAttribute) GetType() attr.Type {
190+
if a.CustomType != nil {
191+
return a.CustomType
192+
}
193+
194+
return types.ListType{
195+
ElemType: a.NestedObject.Type(),
196+
}
197+
}
198+
199+
// IsComputed returns false because it does not apply to ListResource schemas.
200+
func (a ListNestedAttribute) IsComputed() bool {
201+
return false
202+
}
203+
204+
// IsOptional returns the Optional field value.
205+
func (a ListNestedAttribute) IsOptional() bool {
206+
return a.Optional
207+
}
208+
209+
// IsRequired returns the Required field value.
210+
func (a ListNestedAttribute) IsRequired() bool {
211+
return a.Required
212+
}
213+
214+
// IsSensitive returns false because it does not apply to ListResource schemas.
215+
func (a ListNestedAttribute) IsSensitive() bool {
216+
return false
217+
}
218+
219+
// IsWriteOnly returns false because it does not apply to ListResource schemas.
220+
func (a ListNestedAttribute) IsWriteOnly() bool {
221+
return false
222+
}
223+
224+
// IsRequiredForImport returns false as this behavior is only relevant
225+
// for managed resource identity schema attributes.
226+
func (a ListNestedAttribute) IsRequiredForImport() bool {
227+
return false
228+
}
229+
230+
// IsOptionalForImport returns false as this behavior is only relevant
231+
// for managed resource identity schema attributes.
232+
func (a ListNestedAttribute) IsOptionalForImport() bool {
233+
return false
234+
}
235+
236+
// ListValidators returns the Validators field value.
237+
func (a ListNestedAttribute) ListValidators() []validator.List {
238+
return a.Validators
239+
}
240+
241+
// ValidateImplementation contains logic for validating the
242+
// provider-defined implementation of the attribute to prevent unexpected
243+
// errors or panics. This logic runs during the GetProviderSchema RPC and
244+
// should never include false positives.
245+
func (a ListNestedAttribute) ValidateImplementation(ctx context.Context, req fwschema.ValidateImplementationRequest, resp *fwschema.ValidateImplementationResponse) {
246+
if a.CustomType == nil && fwtype.ContainsCollectionWithDynamic(a.GetType()) {
247+
resp.Diagnostics.Append(fwtype.AttributeCollectionWithDynamicTypeDiag(req.Path))
248+
}
249+
}

0 commit comments

Comments
 (0)