From f6c30711c072881e67debc13d27c8da1f6c281c3 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 09:53:45 +0000 Subject: [PATCH 01/11] Adding ValueFromFramework and ToFrameworkValue interface functions to types.ListType and types.List (value), respectively (#535) --- internal/fwserver/attr_value.go | 9 +++--- .../fwserver/attribute_plan_modification.go | 6 ++-- internal/fwserver/attribute_validation.go | 13 ++++++-- internal/fwserver/block_plan_modification.go | 6 ++-- .../fwserver/block_plan_modification_test.go | 2 +- internal/fwserver/block_validation.go | 15 ++++++--- types/list.go | 32 ++++++++++++++++--- 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 8ee6dd8e6..5c86e2fc3 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -3,16 +3,17 @@ 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) { - list, ok := value.(types.List) +func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.List, diag.Diagnostics) { + listVal, ok := value.(types.ListVal) if !ok { return types.ListNull(nil), diag.Diagnostics{ @@ -20,7 +21,7 @@ func coerceListValue(schemaPath path.Path, value attr.Value) (types.List, diag.D } } - return list, nil + return listVal.ToFrameworkValue(ctx) } func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) { diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index c28f77f20..a10fe29dd 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -98,7 +98,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo nm := a.GetAttributes().GetNestingMode() switch nm { case fwschema.NestingModeList: - configList, diags := coerceListValue(req.AttributePath, req.AttributeConfig) + configList, diags := coerceListValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -106,7 +106,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planList, diags := coerceListValue(req.AttributePath, req.AttributePlan) + planList, diags := coerceListValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -114,7 +114,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - stateList, diags := coerceListValue(req.AttributePath, req.AttributeState) + stateList, diags := coerceListValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index b781053b4..7d3979383 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -140,19 +140,26 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute nm := a.GetAttributes().GetNestingMode() switch nm { case fwschema.NestingModeList: - l, ok := req.AttributeConfig.(types.List) + listVal, ok := req.AttributeConfig.(types.ListVal) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Attribute Validation Error", - "Attribute validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Attribute Validation Error Invalid Value Type", + "A type from which a types.List can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + l, diags := listVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for idx := range l.Elements() { for nestedName, nestedAttr := range a.GetAttributes().GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index 453ba7add..6891cb344 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -64,7 +64,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr nm := b.GetNestingMode() switch nm { case fwschema.BlockNestingModeList: - configList, diags := coerceListValue(req.AttributePath, req.AttributeConfig) + configList, diags := coerceListValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -72,7 +72,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planList, diags := coerceListValue(req.AttributePath, req.AttributePlan) + planList, diags := coerceListValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -80,7 +80,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - stateList, diags := coerceListValue(req.AttributePath, req.AttributeState) + stateList, diags := coerceListValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/block_plan_modification_test.go b/internal/fwserver/block_plan_modification_test.go index a34f242dc..8f023d310 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -2013,7 +2013,7 @@ func TestBlockModifyPlan(t *testing.T) { type testBlockPlanModifierNullList struct{} func (t testBlockPlanModifierNullList) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { - _, ok := req.AttributePlan.(types.List) + _, ok := req.AttributePlan.(types.ListVal) if !ok { return } diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index 77d929a86..16bd5ae71 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -49,19 +49,26 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr nm := b.GetNestingMode() switch nm { case fwschema.BlockNestingModeList: - l, ok := req.AttributeConfig.(types.List) + listVal, ok := req.AttributeConfig.(types.ListVal) if !ok { - err := fmt.Errorf("unknown block value type (%s) for nesting mode (%T) at path: %s", req.AttributeConfig.Type(ctx), nm, req.AttributePath) + err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Block Validation Error", - "Block validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Block Validation Error Invalid Value Type", + "A type from which a types.List can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + l, diags := listVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for idx := range l.Elements() { for name, attr := range b.GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ diff --git a/types/list.go b/types/list.go index 1e33fd5ca..7fb73ca64 100644 --- a/types/list.go +++ b/types/list.go @@ -15,10 +15,22 @@ import ( ) var ( - _ attr.Type = ListType{} - _ attr.Value = &List{} + _ ListTyp = ListType{} + _ ListVal = &List{} ) +type ListTyp interface { + attr.Type + + ValueFromFramework(context.Context, List) (attr.Value, diag.Diagnostics) +} + +type ListVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (List, diag.Diagnostics) +} + // ListType is an AttributeType representing a list of values. All values must // be of the same type, which the provider must specify as the ElemType // property. @@ -159,12 +171,17 @@ func (l ListType) Validate(ctx context.Context, in tftypes.Value, path path.Path } // ValueType returns the Value type. -func (t ListType) ValueType(_ context.Context) attr.Value { +func (l ListType) ValueType(_ context.Context) attr.Value { return List{ - elementType: t.ElemType, + elementType: l.ElemType, } } +// ValueFromFramework returns an attr.Value given a List. +func (l ListType) ValueFromFramework(_ context.Context, list List) (attr.Value, diag.Diagnostics) { + return list, nil +} + // ListNull creates a List with a null value. Determine whether the value is // null via the List type IsNull method. func ListNull(elementType attr.Type) List { @@ -249,7 +266,7 @@ func ListValueFrom(ctx context.Context, elementType attr.Type, elements any) (Li // type Elements or ElementsAs methods. // // This creation function is only recommended to create List values which will -// not potentially effect practitioners, such as testing, or exhaustively +// not potentially affect practitioners, such as testing, or exhaustively // tested provider logic. func ListValueMust(elementType attr.Type, elements []attr.Value) List { list, diags := ListValue(elementType, elements) @@ -427,3 +444,8 @@ func (l List) String() string { return res.String() } + +// ToFrameworkValue returns the List. +func (l List) ToFrameworkValue(context.Context) (List, diag.Diagnostics) { + return l, nil +} From 467f056fc4853dbdb65335f424cc2853f54c618b Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 10:05:58 +0000 Subject: [PATCH 02/11] Adding ValueFromFramework and ToFrameworkValue interface functions to types.MapType and types.Map (value), respectively (#535) --- internal/fwserver/attr_value.go | 6 ++-- .../fwserver/attribute_plan_modification.go | 6 ++-- internal/fwserver/attribute_validation.go | 13 ++++++-- types/map.go | 32 ++++++++++++++++--- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 5c86e2fc3..9bb36f560 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -24,8 +24,8 @@ func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value return listVal.ToFrameworkValue(ctx) } -func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) { - m, ok := value.(types.Map) +func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) { + mapVal, ok := value.(types.MapVal) if !ok { return types.MapNull(nil), diag.Diagnostics{ @@ -33,7 +33,7 @@ func coerceMapValue(schemaPath path.Path, value attr.Value) (types.Map, diag.Dia } } - return m, nil + return mapVal.ToFrameworkValue(ctx) } func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index a10fe29dd..26bb49281 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -342,7 +342,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } case fwschema.NestingModeMap: - configMap, diags := coerceMapValue(req.AttributePath, req.AttributeConfig) + configMap, diags := coerceMapValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -350,7 +350,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planMap, diags := coerceMapValue(req.AttributePath, req.AttributePlan) + planMap, diags := coerceMapValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -358,7 +358,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - stateMap, diags := coerceMapValue(req.AttributePath, req.AttributeState) + stateMap, diags := coerceMapValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 7d3979383..3cc0a35db 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -207,19 +207,26 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeMap: - m, ok := req.AttributeConfig.(types.Map) + mapVal, ok := req.AttributeConfig.(types.MapVal) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Attribute Validation Error", - "Attribute validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Attribute Validation Error Invalid Value Type", + "A type from which a types.Map can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + m, diags := mapVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for key := range m.Elements() { for nestedName, nestedAttr := range a.GetAttributes().GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ diff --git a/types/map.go b/types/map.go index a9c1443c0..04202645b 100644 --- a/types/map.go +++ b/types/map.go @@ -16,10 +16,22 @@ import ( ) var ( - _ attr.Type = MapType{} - _ attr.Value = &Map{} + _ MapTyp = MapType{} + _ MapVal = &Map{} ) +type MapTyp interface { + attr.Type + + ValueFromFramework(context.Context, Map) (attr.Value, diag.Diagnostics) +} + +type MapVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Map, diag.Diagnostics) +} + // MapType is an AttributeType representing a map of values. All values must // be of the same type, which the provider must specify as the ElemType // property. Keys will always be strings. @@ -163,12 +175,17 @@ func (m MapType) Validate(ctx context.Context, in tftypes.Value, path path.Path) } // ValueType returns the Value type. -func (t MapType) ValueType(_ context.Context) attr.Value { +func (m MapType) ValueType(_ context.Context) attr.Value { return Map{ - elementType: t.ElemType, + elementType: m.ElemType, } } +// ValueFromFramework returns an attr.Value given a Map. +func (m MapType) ValueFromFramework(_ context.Context, ma Map) (attr.Value, diag.Diagnostics) { + return ma, nil +} + // MapNull creates a Map with a null value. Determine whether the value is // null via the Map type IsNull method. func MapNull(elementType attr.Type) Map { @@ -329,7 +346,7 @@ func (m Map) Type(ctx context.Context) attr.Type { return MapType{ElemType: m.ElementType(ctx)} } -// ToTerraformValue returns the data contained in the List as a tftypes.Value. +// ToTerraformValue returns the data contained in the Map as a tftypes.Value. func (m Map) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { mapType := tftypes.Map{ElementType: m.ElementType(ctx).TerraformType(ctx)} @@ -441,3 +458,8 @@ func (m Map) String() string { return res.String() } + +// ToFrameworkValue returns the Map. +func (m Map) ToFrameworkValue(context.Context) (Map, diag.Diagnostics) { + return m, nil +} From 94d03e6d3ef2e61b90e65b13a7d8c4fa87100528 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 10:28:48 +0000 Subject: [PATCH 03/11] Adding ValueFromFramework and ToFrameworkValue interface functions to types.SetType and types.Set (value), respectively (#535) --- internal/fwserver/attr_value.go | 6 ++-- .../fwserver/attribute_plan_modification.go | 6 ++-- internal/fwserver/attribute_validation.go | 13 ++++++-- internal/fwserver/block_plan_modification.go | 6 ++-- internal/fwserver/block_validation.go | 15 +++++++--- types/set.go | 30 ++++++++++++++++--- 6 files changed, 56 insertions(+), 20 deletions(-) diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 9bb36f560..3fd9c2ec5 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -48,8 +48,8 @@ func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, di return object, nil } -func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { - set, ok := value.(types.Set) +func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { + setVal, ok := value.(types.SetVal) if !ok { return types.SetNull(nil), diag.Diagnostics{ @@ -57,7 +57,7 @@ func coerceSetValue(schemaPath path.Path, value attr.Value) (types.Set, diag.Dia } } - return set, nil + return setVal.ToFrameworkValue(ctx) } func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index 26bb49281..6ed801593 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -220,7 +220,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } case fwschema.NestingModeSet: - configSet, diags := coerceSetValue(req.AttributePath, req.AttributeConfig) + configSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -228,7 +228,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planSet, diags := coerceSetValue(req.AttributePath, req.AttributePlan) + planSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -236,7 +236,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - stateSet, diags := coerceSetValue(req.AttributePath, req.AttributeState) + stateSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 3cc0a35db..f4b3bbfd3 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -177,19 +177,26 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeSet: - s, ok := req.AttributeConfig.(types.Set) + setVal, ok := req.AttributeConfig.(types.SetVal) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Attribute Validation Error", - "Attribute validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Attribute Validation Error Invalid Value Type", + "A type from which a types.Set can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + s, diags := setVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for _, value := range s.Elements() { for nestedName, nestedAttr := range a.GetAttributes().GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index 6891cb344..db8574f14 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -236,7 +236,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } case fwschema.BlockNestingModeSet: - configSet, diags := coerceSetValue(req.AttributePath, req.AttributeConfig) + configSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -244,7 +244,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planSet, diags := coerceSetValue(req.AttributePath, req.AttributePlan) + planSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -252,7 +252,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - stateSet, diags := coerceSetValue(req.AttributePath, req.AttributeState) + stateSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index 16bd5ae71..3eac5c8d0 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -124,19 +124,26 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), len(l.Elements()))) } case fwschema.BlockNestingModeSet: - s, ok := req.AttributeConfig.(types.Set) + setVal, ok := req.AttributeConfig.(types.SetVal) if !ok { - err := fmt.Errorf("unknown block value type (%s) for nesting mode (%T) at path: %s", req.AttributeConfig.Type(ctx), nm, req.AttributePath) + err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Block Validation Error", - "Block validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Block Validation Error Invalid Value Type", + "A type from which a types.Set can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + s, diags := setVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for _, value := range s.Elements() { for name, attr := range b.GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ diff --git a/types/set.go b/types/set.go index 18c386d71..581a12c17 100644 --- a/types/set.go +++ b/types/set.go @@ -15,11 +15,23 @@ import ( ) var ( - _ attr.Type = SetType{} + _ SetTyp = SetType{} _ xattr.TypeWithValidate = SetType{} - _ attr.Value = &Set{} + _ SetVal = &Set{} ) +type SetTyp interface { + attr.Type + + ValueFromFramework(context.Context, Set) (attr.Value, diag.Diagnostics) +} + +type SetVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Set, diag.Diagnostics) +} + // SetType is an AttributeType representing a set of values. All values must // be of the same type, which the provider must specify as the ElemType // property. @@ -191,12 +203,17 @@ func (st SetType) Validate(ctx context.Context, in tftypes.Value, path path.Path } // ValueType returns the Value type. -func (t SetType) ValueType(_ context.Context) attr.Value { +func (st SetType) ValueType(_ context.Context) attr.Value { return Set{ - elementType: t.ElemType, + elementType: st.ElemType, } } +// ValueFromFramework returns an attr.Value given a Set. +func (st SetType) ValueFromFramework(_ context.Context, set Set) (attr.Value, diag.Diagnostics) { + return set, nil +} + // SetNull creates a Set with a null value. Determine whether the value is // null via the Set type IsNull method. func SetNull(elementType attr.Type) Set { @@ -467,3 +484,8 @@ func (s Set) String() string { return res.String() } + +// ToFrameworkValue returns the Set. +func (s Set) ToFrameworkValue(context.Context) (Set, diag.Diagnostics) { + return s, nil +} From a042afc0849f6d52b9015b841b4a658b8fed320d Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 10:45:45 +0000 Subject: [PATCH 04/11] Adding ValueFromFramework and ToFrameworkValue interface functions to types.ObjectType and types.Object (value), respectively (#535) --- internal/fwserver/attr_value.go | 18 +++++------ .../fwserver/attribute_plan_modification.go | 12 ++++---- internal/fwserver/attribute_validation.go | 13 ++++++-- internal/fwserver/block_plan_modification.go | 10 +++---- internal/fwserver/block_validation.go | 17 +++++++---- types/object.go | 30 ++++++++++++++++--- 6 files changed, 68 insertions(+), 32 deletions(-) diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 3fd9c2ec5..117f949b4 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -36,8 +36,8 @@ func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) return mapVal.ToFrameworkValue(ctx) } -func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { - object, ok := value.(types.Object) +func coerceObjectValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { + objectVal, ok := value.(types.ObjectVal) if !ok { return types.ObjectNull(nil), diag.Diagnostics{ @@ -45,7 +45,7 @@ func coerceObjectValue(schemaPath path.Path, value attr.Value) (types.Object, di } } - return object, nil + return objectVal.ToFrameworkValue(ctx) } func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { @@ -73,7 +73,7 @@ func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, return listElemObjectFromTerraformValue(ctx, schemaPath, list, description, nil) } - return coerceObjectValue(schemaPath, list.Elements()[index]) + return coerceObjectValue(ctx, schemaPath, list.Elements()[index]) } func listElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, list types.List, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { @@ -86,7 +86,7 @@ func listElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, } } - return coerceObjectValue(schemaPath, elemValue) + return coerceObjectValue(ctx, schemaPath, elemValue) } func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key string, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { @@ -104,7 +104,7 @@ func mapElemObject(ctx context.Context, schemaPath path.Path, m types.Map, key s return mapElemObjectFromTerraformValue(ctx, schemaPath, m, description, nil) } - return coerceObjectValue(schemaPath, elemValue) + return coerceObjectValue(ctx, schemaPath, elemValue) } func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, m types.Map, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { @@ -117,7 +117,7 @@ func mapElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, } } - return coerceObjectValue(schemaPath, elemValue) + return coerceObjectValue(ctx, schemaPath, elemValue) } func objectAttributeValue(ctx context.Context, object types.Object, attributeName string, description fwschemadata.DataDescription) (attr.Value, diag.Diagnostics) { @@ -163,7 +163,7 @@ func setElemObject(ctx context.Context, schemaPath path.Path, set types.Set, ind return setElemObjectFromTerraformValue(ctx, schemaPath, set, description, nil) } - return coerceObjectValue(schemaPath, set.Elements()[index]) + return coerceObjectValue(ctx, schemaPath, set.Elements()[index]) } func setElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, set types.Set, description fwschemadata.DataDescription, tfValue any) (types.Object, diag.Diagnostics) { @@ -176,5 +176,5 @@ func setElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path, } } - return coerceObjectValue(schemaPath, elemValue) + return coerceObjectValue(ctx, schemaPath, elemValue) } diff --git a/internal/fwserver/attribute_plan_modification.go b/internal/fwserver/attribute_plan_modification.go index 6ed801593..8dfd3f970 100644 --- a/internal/fwserver/attribute_plan_modification.go +++ b/internal/fwserver/attribute_plan_modification.go @@ -135,7 +135,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObject, diags := coerceObjectValue(attrPath, planElem) + planObject, diags := coerceObjectValue(ctx, attrPath, planElem) resp.Diagnostics.Append(diags...) @@ -257,7 +257,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObject, diags := coerceObjectValue(attrPath, planElem) + planObject, diags := coerceObjectValue(ctx, attrPath, planElem) resp.Diagnostics.Append(diags...) @@ -379,7 +379,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObject, diags := coerceObjectValue(attrPath, planElem) + planObject, diags := coerceObjectValue(ctx, attrPath, planElem) resp.Diagnostics.Append(diags...) @@ -464,7 +464,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } case fwschema.NestingModeSingle: - configObject, diags := coerceObjectValue(req.AttributePath, req.AttributeConfig) + configObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -472,7 +472,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - planObject, diags := coerceObjectValue(req.AttributePath, req.AttributePlan) + planObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -480,7 +480,7 @@ func AttributeModifyPlan(ctx context.Context, a fwschema.Attribute, req tfsdk.Mo return } - stateObject, diags := coerceObjectValue(req.AttributePath, req.AttributeState) + stateObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index f4b3bbfd3..475979dab 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -251,19 +251,26 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeSingle: - o, ok := req.AttributeConfig.(types.Object) + objectVal, ok := req.AttributeConfig.(types.ObjectVal) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Attribute Validation Error", - "Attribute validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Attribute Validation Error Invalid Value Type", + "A type from which a types.Object can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + o, diags := objectVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + if o.IsNull() || o.IsUnknown() { return } diff --git a/internal/fwserver/block_plan_modification.go b/internal/fwserver/block_plan_modification.go index db8574f14..ae935266c 100644 --- a/internal/fwserver/block_plan_modification.go +++ b/internal/fwserver/block_plan_modification.go @@ -101,7 +101,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planObject, diags := coerceObjectValue(attrPath, planElem) + planObject, diags := coerceObjectValue(ctx, attrPath, planElem) resp.Diagnostics.Append(diags...) @@ -273,7 +273,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planObject, diags := coerceObjectValue(attrPath, planElem) + planObject, diags := coerceObjectValue(ctx, attrPath, planElem) resp.Diagnostics.Append(diags...) @@ -408,7 +408,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } case fwschema.BlockNestingModeSingle: - configObject, diags := coerceObjectValue(req.AttributePath, req.AttributeConfig) + configObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributeConfig) resp.Diagnostics.Append(diags...) @@ -416,7 +416,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - planObject, diags := coerceObjectValue(req.AttributePath, req.AttributePlan) + planObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributePlan) resp.Diagnostics.Append(diags...) @@ -424,7 +424,7 @@ func BlockModifyPlan(ctx context.Context, b fwschema.Block, req tfsdk.ModifyAttr return } - stateObject, diags := coerceObjectValue(req.AttributePath, req.AttributeState) + stateObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributeState) resp.Diagnostics.Append(diags...) diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index 3eac5c8d0..b867dcb61 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -199,19 +199,26 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), len(s.Elements()))) } case fwschema.BlockNestingModeSingle: - s, ok := req.AttributeConfig.(types.Object) + objectVal, ok := req.AttributeConfig.(types.ObjectVal) if !ok { - err := fmt.Errorf("unknown block value type (%s) for nesting mode (%T) at path: %s", req.AttributeConfig.Type(ctx), nm, req.AttributePath) + err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) resp.Diagnostics.AddAttributeError( req.AttributePath, - "Block Validation Error", - "Block validation cannot walk schema. Report this to the provider developer:\n\n"+err.Error(), + "Block Validation Error Invalid Value Type", + "A type from which a types.Object can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return } + o, diags := objectVal.ToFrameworkValue(ctx) + + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + for name, attr := range b.GetAttributes() { nestedAttrReq := tfsdk.ValidateAttributeRequest{ AttributePath: req.AttributePath.AtName(name), @@ -242,7 +249,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 && o.IsNull() { resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), 0)) } default: diff --git a/types/object.go b/types/object.go index f834be36b..f0b25c88b 100644 --- a/types/object.go +++ b/types/object.go @@ -15,10 +15,22 @@ import ( ) var ( - _ attr.Type = ObjectType{} - _ attr.Value = &Object{} + _ ObjectTyp = ObjectType{} + _ ObjectVal = &Object{} ) +type ObjectTyp interface { + attr.Type + + ValueFromFramework(context.Context, Object) (attr.Value, diag.Diagnostics) +} + +type ObjectVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Object, diag.Diagnostics) +} + // ObjectType is an AttributeType representing an object. type ObjectType struct { AttrTypes map[string]attr.Type @@ -141,12 +153,17 @@ func (o ObjectType) String() string { } // ValueType returns the Value type. -func (t ObjectType) ValueType(_ context.Context) attr.Value { +func (o ObjectType) ValueType(_ context.Context) attr.Value { return Object{ - attributeTypes: t.AttrTypes, + attributeTypes: o.AttrTypes, } } +// ValueFromFramework returns an attr.Value given an Object. +func (o ObjectType) ValueFromFramework(_ context.Context, obj Object) (attr.Value, diag.Diagnostics) { + return obj, nil +} + // ObjectNull creates a Object with a null value. Determine whether the value is // null via the Object type IsNull method. func ObjectNull(attributeTypes map[string]attr.Type) Object { @@ -480,3 +497,8 @@ func (o Object) String() string { return res.String() } + +// ToFrameworkValue returns the Object. +func (o Object) ToFrameworkValue(context.Context) (Object, diag.Diagnostics) { + return o, nil +} From 69b5b100dbfa6ab64e2e5cd15fbdc2bbb94e591e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 14:26:11 +0000 Subject: [PATCH 05/11] Adding ValueFromFramework and ToFrameworkValue interface functions to primitive types and primitive values, respectively (#535) --- types/bool.go | 15 ++++++++++++++- types/float64.go | 16 ++++++++++++++-- types/int64.go | 16 ++++++++++++++-- types/number.go | 17 ++++++++++++++-- types/primitive.go | 48 ++++++++++++++++++++++++++++++++++++++++------ types/string.go | 17 ++++++++++++++-- 6 files changed, 114 insertions(+), 15 deletions(-) diff --git a/types/bool.go b/types/bool.go index e97beafa9..95f303e21 100644 --- a/types/bool.go +++ b/types/bool.go @@ -5,13 +5,21 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-go/tftypes" ) var ( - _ attr.Value = Bool{} + _ BoolVal = Bool{} ) +type BoolVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Bool, diag.Diagnostics) +} + // BoolNull creates a Bool with a null value. Determine whether the value is // null via the Bool type IsNull method. // @@ -143,3 +151,8 @@ func (b Bool) String() string { func (b Bool) ValueBool() bool { return b.value } + +// ToFrameworkValue returns Bool. +func (b Bool) ToFrameworkValue(context.Context) (Bool, diag.Diagnostics) { + return b, nil +} diff --git a/types/float64.go b/types/float64.go index 38382e2ac..5e31cfbee 100644 --- a/types/float64.go +++ b/types/float64.go @@ -5,16 +5,23 @@ import ( "fmt" "math/big" + "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/path" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var ( - _ attr.Value = Float64{} + _ Float64Val = Float64{} ) +type Float64Val interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Float64, diag.Diagnostics) +} + // Float64Null creates a Float64 with a null value. Determine whether the value is // null via the Float64 type IsNull method. // @@ -204,3 +211,8 @@ func (f Float64) String() string { func (f Float64) ValueFloat64() float64 { return f.value } + +// ToFrameworkValue returns Float64. +func (f Float64) ToFrameworkValue(context.Context) (Float64, diag.Diagnostics) { + return f, nil +} diff --git a/types/int64.go b/types/int64.go index cda43fbe0..e09d6a978 100644 --- a/types/int64.go +++ b/types/int64.go @@ -5,16 +5,23 @@ import ( "fmt" "math/big" + "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/path" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) var ( - _ attr.Value = Int64{} + _ Int64Val = Int64{} ) +type Int64Val interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Int64, diag.Diagnostics) +} + // Int64Null creates a Int64 with a null value. Determine whether the value is // null via the Int64 type IsNull method. // @@ -217,3 +224,8 @@ func (i Int64) String() string { func (i Int64) ValueInt64() int64 { return i.value } + +// ToFrameworkValue returns Int64. +func (i Int64) ToFrameworkValue(context.Context) (Int64, diag.Diagnostics) { + return i, nil +} diff --git a/types/number.go b/types/number.go index bac347d15..1c427053f 100644 --- a/types/number.go +++ b/types/number.go @@ -5,14 +5,22 @@ import ( "fmt" "math/big" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" ) var ( - _ attr.Value = Number{} + _ NumberVal = Number{} ) +type NumberVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (Number, diag.Diagnostics) +} + // NumberNull creates a Number with a null value. Determine whether the value is // null via the Number type IsNull method. // @@ -153,3 +161,8 @@ func (n Number) String() string { func (n Number) ValueBigFloat() *big.Float { return n.value } + +// ToFrameworkValue returns Number. +func (n Number) ToFrameworkValue(context.Context) (Number, diag.Diagnostics) { + return n, nil +} diff --git a/types/primitive.go b/types/primitive.go index bdadd153f..754657d3e 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -4,11 +4,12 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/attr/xattr" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) type primitive uint8 @@ -31,13 +32,48 @@ const ( ) var ( - _ attr.Type = StringType - _ attr.Type = NumberType - _ attr.Type = BoolType - _ xattr.TypeWithValidate = Int64Type - _ xattr.TypeWithValidate = Float64Type + _ StringTyp = StringType + _ NumberTyp = NumberType + _ BoolTyp = BoolType + _ Int64Typ = Int64Type + _ Float64Typ = Float64Type ) +type StringTyp interface { + attr.Type + + ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) +} + +type NumberTyp interface { + attr.Type + + ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) +} + +type BoolTyp interface { + attr.Type + + ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) +} + +type Int64Typ interface { + xattr.TypeWithValidate + + ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) +} + +type Float64Typ interface { + xattr.TypeWithValidate + + ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) +} + +// ValueFromFramework returns an attr.Value given an attr.Value. +func (p primitive) ValueFromFramework(_ context.Context, val attr.Value) (attr.Value, diag.Diagnostics) { + return val, nil +} + func (p primitive) String() string { switch p { case StringType: diff --git a/types/string.go b/types/string.go index 9239cb403..29a024f77 100644 --- a/types/string.go +++ b/types/string.go @@ -4,14 +4,22 @@ import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-go/tftypes" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" ) var ( - _ attr.Value = String{} + _ StringVal = String{} ) +type StringVal interface { + attr.Value + + ToFrameworkValue(ctx context.Context) (String, diag.Diagnostics) +} + // StringNull creates a String with a null value. Determine whether the value is // null via the String type IsNull method. // @@ -145,3 +153,8 @@ func (s String) String() string { func (s String) ValueString() string { return s.value } + +// ToFrameworkValue returns String. +func (s String) ToFrameworkValue(context.Context) (String, diag.Diagnostics) { + return s, nil +} From ddaa77bb7b561fd034232171ed641597089c86d3 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Fri, 11 Nov 2022 17:18:21 +0000 Subject: [PATCH 06/11] Refactoring interface names to use -able suffix (#535) --- internal/fwserver/attr_value.go | 16 +++--- internal/fwserver/attribute_validation.go | 16 +++--- .../fwserver/block_plan_modification_test.go | 2 +- internal/fwserver/block_validation.go | 12 ++-- types/bool.go | 10 ++-- types/float64.go | 10 ++-- types/int64.go | 10 ++-- types/list.go | 20 +++---- types/map.go | 20 +++---- types/number.go | 10 ++-- types/object.go | 20 +++---- types/primitive.go | 56 +++++++++++++------ types/set.go | 20 +++---- types/string.go | 10 ++-- 14 files changed, 126 insertions(+), 106 deletions(-) diff --git a/internal/fwserver/attr_value.go b/internal/fwserver/attr_value.go index 117f949b4..a2e855236 100644 --- a/internal/fwserver/attr_value.go +++ b/internal/fwserver/attr_value.go @@ -13,7 +13,7 @@ import ( ) func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.List, diag.Diagnostics) { - listVal, ok := value.(types.ListVal) + listVal, ok := value.(types.ListValuable) if !ok { return types.ListNull(nil), diag.Diagnostics{ @@ -21,11 +21,11 @@ func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value } } - return listVal.ToFrameworkValue(ctx) + return listVal.ToListValue(ctx) } func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) { - mapVal, ok := value.(types.MapVal) + mapVal, ok := value.(types.MapValuable) if !ok { return types.MapNull(nil), diag.Diagnostics{ @@ -33,11 +33,11 @@ func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) } } - return mapVal.ToFrameworkValue(ctx) + return mapVal.ToMapValue(ctx) } func coerceObjectValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) { - objectVal, ok := value.(types.ObjectVal) + objectVal, ok := value.(types.ObjectValuable) if !ok { return types.ObjectNull(nil), diag.Diagnostics{ @@ -45,11 +45,11 @@ func coerceObjectValue(ctx context.Context, schemaPath path.Path, value attr.Val } } - return objectVal.ToFrameworkValue(ctx) + return objectVal.ToObjectValue(ctx) } func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) { - setVal, ok := value.(types.SetVal) + setVal, ok := value.(types.SetValuable) if !ok { return types.SetNull(nil), diag.Diagnostics{ @@ -57,7 +57,7 @@ func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) } } - return setVal.ToFrameworkValue(ctx) + return setVal.ToSetValue(ctx) } func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) { diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 475979dab..7483dbf19 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -140,7 +140,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute nm := a.GetAttributes().GetNestingMode() switch nm { case fwschema.NestingModeList: - listVal, ok := req.AttributeConfig.(types.ListVal) + listVal, ok := req.AttributeConfig.(types.ListValuable) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -153,7 +153,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute return } - l, diags := listVal.ToFrameworkValue(ctx) + l, diags := listVal.ToListValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -177,7 +177,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeSet: - setVal, ok := req.AttributeConfig.(types.SetVal) + setVal, ok := req.AttributeConfig.(types.SetValuable) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -190,7 +190,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute return } - s, diags := setVal.ToFrameworkValue(ctx) + s, diags := setVal.ToSetValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -214,7 +214,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeMap: - mapVal, ok := req.AttributeConfig.(types.MapVal) + mapVal, ok := req.AttributeConfig.(types.MapValuable) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -227,7 +227,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute return } - m, diags := mapVal.ToFrameworkValue(ctx) + m, diags := mapVal.ToMapValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -251,7 +251,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute } } case fwschema.NestingModeSingle: - objectVal, ok := req.AttributeConfig.(types.ObjectVal) + objectVal, ok := req.AttributeConfig.(types.ObjectValuable) if !ok { err := fmt.Errorf("unknown attribute value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -264,7 +264,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute return } - o, diags := objectVal.ToFrameworkValue(ctx) + o, diags := objectVal.ToObjectValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/internal/fwserver/block_plan_modification_test.go b/internal/fwserver/block_plan_modification_test.go index 8f023d310..20a67d6e4 100644 --- a/internal/fwserver/block_plan_modification_test.go +++ b/internal/fwserver/block_plan_modification_test.go @@ -2013,7 +2013,7 @@ func TestBlockModifyPlan(t *testing.T) { type testBlockPlanModifierNullList struct{} func (t testBlockPlanModifierNullList) Modify(ctx context.Context, req tfsdk.ModifyAttributePlanRequest, resp *tfsdk.ModifyAttributePlanResponse) { - _, ok := req.AttributePlan.(types.ListVal) + _, ok := req.AttributePlan.(types.ListValuable) if !ok { return } diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index b867dcb61..f2496d79c 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -49,7 +49,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr nm := b.GetNestingMode() switch nm { case fwschema.BlockNestingModeList: - listVal, ok := req.AttributeConfig.(types.ListVal) + listVal, ok := req.AttributeConfig.(types.ListValuable) if !ok { err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -62,7 +62,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr return } - l, diags := listVal.ToFrameworkValue(ctx) + l, diags := listVal.ToListValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -124,7 +124,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), len(l.Elements()))) } case fwschema.BlockNestingModeSet: - setVal, ok := req.AttributeConfig.(types.SetVal) + setVal, ok := req.AttributeConfig.(types.SetValuable) if !ok { err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -137,7 +137,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr return } - s, diags := setVal.ToFrameworkValue(ctx) + s, diags := setVal.ToSetValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -199,7 +199,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.Append(blockMinItemsDiagnostic(req.AttributePath, b.GetMinItems(), len(s.Elements()))) } case fwschema.BlockNestingModeSingle: - objectVal, ok := req.AttributeConfig.(types.ObjectVal) + objectVal, ok := req.AttributeConfig.(types.ObjectValuable) if !ok { err := fmt.Errorf("unknown block value type (%T) for nesting mode (%T) at path: %s", req.AttributeConfig, nm, req.AttributePath) @@ -212,7 +212,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr return } - o, diags := objectVal.ToFrameworkValue(ctx) + o, diags := objectVal.ToObjectValue(ctx) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { diff --git a/types/bool.go b/types/bool.go index 95f303e21..d635f0cee 100644 --- a/types/bool.go +++ b/types/bool.go @@ -11,13 +11,13 @@ import ( ) var ( - _ BoolVal = Bool{} + _ BoolValuable = Bool{} ) -type BoolVal interface { +type BoolValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Bool, diag.Diagnostics) + ToBoolValue(ctx context.Context) (Bool, diag.Diagnostics) } // BoolNull creates a Bool with a null value. Determine whether the value is @@ -152,7 +152,7 @@ func (b Bool) ValueBool() bool { return b.value } -// ToFrameworkValue returns Bool. -func (b Bool) ToFrameworkValue(context.Context) (Bool, diag.Diagnostics) { +// ToBoolValue returns Bool. +func (b Bool) ToBoolValue(context.Context) (Bool, diag.Diagnostics) { return b, nil } diff --git a/types/float64.go b/types/float64.go index 5e31cfbee..e4949c227 100644 --- a/types/float64.go +++ b/types/float64.go @@ -13,13 +13,13 @@ import ( ) var ( - _ Float64Val = Float64{} + _ Float64Valuable = Float64{} ) -type Float64Val interface { +type Float64Valuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Float64, diag.Diagnostics) + ToFloat64Value(ctx context.Context) (Float64, diag.Diagnostics) } // Float64Null creates a Float64 with a null value. Determine whether the value is @@ -212,7 +212,7 @@ func (f Float64) ValueFloat64() float64 { return f.value } -// ToFrameworkValue returns Float64. -func (f Float64) ToFrameworkValue(context.Context) (Float64, diag.Diagnostics) { +// ToFloat64Value returns Float64. +func (f Float64) ToFloat64Value(context.Context) (Float64, diag.Diagnostics) { return f, nil } diff --git a/types/int64.go b/types/int64.go index e09d6a978..406bc84db 100644 --- a/types/int64.go +++ b/types/int64.go @@ -13,13 +13,13 @@ import ( ) var ( - _ Int64Val = Int64{} + _ Int64Valuable = Int64{} ) -type Int64Val interface { +type Int64Valuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Int64, diag.Diagnostics) + ToInt64Value(ctx context.Context) (Int64, diag.Diagnostics) } // Int64Null creates a Int64 with a null value. Determine whether the value is @@ -225,7 +225,7 @@ func (i Int64) ValueInt64() int64 { return i.value } -// ToFrameworkValue returns Int64. -func (i Int64) ToFrameworkValue(context.Context) (Int64, diag.Diagnostics) { +// ToInt64Value returns Int64. +func (i Int64) ToInt64Value(context.Context) (Int64, diag.Diagnostics) { return i, nil } diff --git a/types/list.go b/types/list.go index 7fb73ca64..ba692045a 100644 --- a/types/list.go +++ b/types/list.go @@ -15,20 +15,20 @@ import ( ) var ( - _ ListTyp = ListType{} - _ ListVal = &List{} + _ ListTypable = ListType{} + _ ListValuable = &List{} ) -type ListTyp interface { +type ListTypable interface { attr.Type - ValueFromFramework(context.Context, List) (attr.Value, diag.Diagnostics) + ValueFromList(context.Context, List) (attr.Value, diag.Diagnostics) } -type ListVal interface { +type ListValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (List, diag.Diagnostics) + ToListValue(ctx context.Context) (List, diag.Diagnostics) } // ListType is an AttributeType representing a list of values. All values must @@ -177,8 +177,8 @@ func (l ListType) ValueType(_ context.Context) attr.Value { } } -// ValueFromFramework returns an attr.Value given a List. -func (l ListType) ValueFromFramework(_ context.Context, list List) (attr.Value, diag.Diagnostics) { +// ValueFromList returns an attr.Value given a List. +func (l ListType) ValueFromList(_ context.Context, list List) (attr.Value, diag.Diagnostics) { return list, nil } @@ -445,7 +445,7 @@ func (l List) String() string { return res.String() } -// ToFrameworkValue returns the List. -func (l List) ToFrameworkValue(context.Context) (List, diag.Diagnostics) { +// ToListValue returns the List. +func (l List) ToListValue(context.Context) (List, diag.Diagnostics) { return l, nil } diff --git a/types/map.go b/types/map.go index 04202645b..95df88628 100644 --- a/types/map.go +++ b/types/map.go @@ -16,20 +16,20 @@ import ( ) var ( - _ MapTyp = MapType{} - _ MapVal = &Map{} + _ MapTypable = MapType{} + _ MapValuable = &Map{} ) -type MapTyp interface { +type MapTypable interface { attr.Type - ValueFromFramework(context.Context, Map) (attr.Value, diag.Diagnostics) + ValueFromMap(context.Context, Map) (attr.Value, diag.Diagnostics) } -type MapVal interface { +type MapValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Map, diag.Diagnostics) + ToMapValue(ctx context.Context) (Map, diag.Diagnostics) } // MapType is an AttributeType representing a map of values. All values must @@ -181,8 +181,8 @@ func (m MapType) ValueType(_ context.Context) attr.Value { } } -// ValueFromFramework returns an attr.Value given a Map. -func (m MapType) ValueFromFramework(_ context.Context, ma Map) (attr.Value, diag.Diagnostics) { +// ValueFromMap returns an attr.Value given a Map. +func (m MapType) ValueFromMap(_ context.Context, ma Map) (attr.Value, diag.Diagnostics) { return ma, nil } @@ -459,7 +459,7 @@ func (m Map) String() string { return res.String() } -// ToFrameworkValue returns the Map. -func (m Map) ToFrameworkValue(context.Context) (Map, diag.Diagnostics) { +// ToMapValue returns the Map. +func (m Map) ToMapValue(context.Context) (Map, diag.Diagnostics) { return m, nil } diff --git a/types/number.go b/types/number.go index 1c427053f..091fde063 100644 --- a/types/number.go +++ b/types/number.go @@ -12,13 +12,13 @@ import ( ) var ( - _ NumberVal = Number{} + _ NumberValuable = Number{} ) -type NumberVal interface { +type NumberValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Number, diag.Diagnostics) + ToNumberValue(ctx context.Context) (Number, diag.Diagnostics) } // NumberNull creates a Number with a null value. Determine whether the value is @@ -162,7 +162,7 @@ func (n Number) ValueBigFloat() *big.Float { return n.value } -// ToFrameworkValue returns Number. -func (n Number) ToFrameworkValue(context.Context) (Number, diag.Diagnostics) { +// ToNumberValue returns Number. +func (n Number) ToNumberValue(context.Context) (Number, diag.Diagnostics) { return n, nil } diff --git a/types/object.go b/types/object.go index f0b25c88b..68d521713 100644 --- a/types/object.go +++ b/types/object.go @@ -15,20 +15,20 @@ import ( ) var ( - _ ObjectTyp = ObjectType{} - _ ObjectVal = &Object{} + _ ObjectTypable = ObjectType{} + _ ObjectValuable = &Object{} ) -type ObjectTyp interface { +type ObjectTypable interface { attr.Type - ValueFromFramework(context.Context, Object) (attr.Value, diag.Diagnostics) + ValueFromObject(context.Context, Object) (attr.Value, diag.Diagnostics) } -type ObjectVal interface { +type ObjectValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Object, diag.Diagnostics) + ToObjectValue(ctx context.Context) (Object, diag.Diagnostics) } // ObjectType is an AttributeType representing an object. @@ -159,8 +159,8 @@ func (o ObjectType) ValueType(_ context.Context) attr.Value { } } -// ValueFromFramework returns an attr.Value given an Object. -func (o ObjectType) ValueFromFramework(_ context.Context, obj Object) (attr.Value, diag.Diagnostics) { +// ValueFromObject returns an attr.Value given an Object. +func (o ObjectType) ValueFromObject(_ context.Context, obj Object) (attr.Value, diag.Diagnostics) { return obj, nil } @@ -498,7 +498,7 @@ func (o Object) String() string { return res.String() } -// ToFrameworkValue returns the Object. -func (o Object) ToFrameworkValue(context.Context) (Object, diag.Diagnostics) { +// ToObjectValue returns the Object. +func (o Object) ToObjectValue(context.Context) (Object, diag.Diagnostics) { return o, nil } diff --git a/types/primitive.go b/types/primitive.go index 754657d3e..fff5b6ddc 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -32,46 +32,66 @@ const ( ) var ( - _ StringTyp = StringType - _ NumberTyp = NumberType - _ BoolTyp = BoolType - _ Int64Typ = Int64Type - _ Float64Typ = Float64Type + _ StringTypable = StringType + _ NumberTypable = NumberType + _ BoolTypable = BoolType + _ Int64Typable = Int64Type + _ Float64Typable = Float64Type ) -type StringTyp interface { +type StringTypable interface { attr.Type - ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) + ValueFromString(context.Context, String) (attr.Value, diag.Diagnostics) } -type NumberTyp interface { +type NumberTypable interface { attr.Type - ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) + ValueFromNumber(context.Context, Number) (attr.Value, diag.Diagnostics) } -type BoolTyp interface { +type BoolTypable interface { attr.Type - ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) + ValueFromBool(context.Context, Bool) (attr.Value, diag.Diagnostics) } -type Int64Typ interface { +type Int64Typable interface { xattr.TypeWithValidate - ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) + ValueFromInt64(context.Context, Int64) (attr.Value, diag.Diagnostics) } -type Float64Typ interface { +type Float64Typable interface { xattr.TypeWithValidate - ValueFromFramework(context.Context, attr.Value) (attr.Value, diag.Diagnostics) + ValueFromFloat64(context.Context, Float64) (attr.Value, diag.Diagnostics) } -// ValueFromFramework returns an attr.Value given an attr.Value. -func (p primitive) ValueFromFramework(_ context.Context, val attr.Value) (attr.Value, diag.Diagnostics) { - return val, nil +// ValueFromString returns an attr.Value given a String. +func (p primitive) ValueFromString(_ context.Context, s String) (attr.Value, diag.Diagnostics) { + return s, nil +} + +// ValueFromNumber returns an attr.Value given a Number. +func (p primitive) ValueFromNumber(_ context.Context, n Number) (attr.Value, diag.Diagnostics) { + return n, nil +} + +// ValueFromBool returns an attr.Value given a Bool. +func (p primitive) ValueFromBool(_ context.Context, b Bool) (attr.Value, diag.Diagnostics) { + return b, nil +} + +// ValueFromInt64 returns an attr.Value given a Int64. +func (p primitive) ValueFromInt64(_ context.Context, i Int64) (attr.Value, diag.Diagnostics) { + return i, nil +} + +// ValueFromFloat64 returns an attr.Value given a Float64. +func (p primitive) ValueFromFloat64(_ context.Context, f Float64) (attr.Value, diag.Diagnostics) { + return f, nil } func (p primitive) String() string { diff --git a/types/set.go b/types/set.go index 581a12c17..84ffc2990 100644 --- a/types/set.go +++ b/types/set.go @@ -15,21 +15,21 @@ import ( ) var ( - _ SetTyp = SetType{} + _ SetTypable = SetType{} _ xattr.TypeWithValidate = SetType{} - _ SetVal = &Set{} + _ SetValuable = &Set{} ) -type SetTyp interface { +type SetTypable interface { attr.Type - ValueFromFramework(context.Context, Set) (attr.Value, diag.Diagnostics) + ValueFromSet(context.Context, Set) (attr.Value, diag.Diagnostics) } -type SetVal interface { +type SetValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (Set, diag.Diagnostics) + ToSetValue(ctx context.Context) (Set, diag.Diagnostics) } // SetType is an AttributeType representing a set of values. All values must @@ -209,8 +209,8 @@ func (st SetType) ValueType(_ context.Context) attr.Value { } } -// ValueFromFramework returns an attr.Value given a Set. -func (st SetType) ValueFromFramework(_ context.Context, set Set) (attr.Value, diag.Diagnostics) { +// ValueFromSet returns an attr.Value given a Set. +func (st SetType) ValueFromSet(_ context.Context, set Set) (attr.Value, diag.Diagnostics) { return set, nil } @@ -485,7 +485,7 @@ func (s Set) String() string { return res.String() } -// ToFrameworkValue returns the Set. -func (s Set) ToFrameworkValue(context.Context) (Set, diag.Diagnostics) { +// ToSetValue returns the Set. +func (s Set) ToSetValue(context.Context) (Set, diag.Diagnostics) { return s, nil } diff --git a/types/string.go b/types/string.go index 29a024f77..a528f84e6 100644 --- a/types/string.go +++ b/types/string.go @@ -11,13 +11,13 @@ import ( ) var ( - _ StringVal = String{} + _ StringValuable = String{} ) -type StringVal interface { +type StringValuable interface { attr.Value - ToFrameworkValue(ctx context.Context) (String, diag.Diagnostics) + ToStringValue(ctx context.Context) (String, diag.Diagnostics) } // StringNull creates a String with a null value. Determine whether the value is @@ -154,7 +154,7 @@ func (s String) ValueString() string { return s.value } -// ToFrameworkValue returns String. -func (s String) ToFrameworkValue(context.Context) (String, diag.Diagnostics) { +// ToStringValue returns String. +func (s String) ToStringValue(context.Context) (String, diag.Diagnostics) { return s, nil } From 23bfa1a822a644f3775488b2639f6442725b7c18 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 14 Nov 2022 10:54:13 +0000 Subject: [PATCH 07/11] Adding Go documentation and refactoring to return type-specific interfaces from ValueFrom functions (#535) --- types/bool.go | 2 ++ types/float64.go | 2 ++ types/int64.go | 2 ++ types/list.go | 8 ++++++-- types/map.go | 8 ++++++-- types/number.go | 2 ++ types/object.go | 8 ++++++-- types/primitive.go | 30 ++++++++++++++++++++---------- types/set.go | 8 ++++++-- types/string.go | 2 ++ 10 files changed, 54 insertions(+), 18 deletions(-) diff --git a/types/bool.go b/types/bool.go index d635f0cee..1e92b34f0 100644 --- a/types/bool.go +++ b/types/bool.go @@ -14,6 +14,8 @@ var ( _ BoolValuable = Bool{} ) +// BoolValuable extends attr.Value for bool value types. +// Implement this interface to create a custom Bool value type. type BoolValuable interface { attr.Value diff --git a/types/float64.go b/types/float64.go index e4949c227..a3b229c2d 100644 --- a/types/float64.go +++ b/types/float64.go @@ -16,6 +16,8 @@ var ( _ Float64Valuable = Float64{} ) +// Float64Valuable extends attr.Value for float64 value types. +// Implement this interface to create a custom Float64 value type. type Float64Valuable interface { attr.Value diff --git a/types/int64.go b/types/int64.go index 406bc84db..7e152c472 100644 --- a/types/int64.go +++ b/types/int64.go @@ -16,6 +16,8 @@ var ( _ Int64Valuable = Int64{} ) +// Int64Valuable extends attr.Value for int64 value types. +// Implement this interface to create a custom Int64 value type. type Int64Valuable interface { attr.Value diff --git a/types/list.go b/types/list.go index ba692045a..307ca2d31 100644 --- a/types/list.go +++ b/types/list.go @@ -19,12 +19,16 @@ var ( _ ListValuable = &List{} ) +// ListTypable extends attr.Type for list type types. +// Implement this interface to create a custom ListType type type. type ListTypable interface { attr.Type - ValueFromList(context.Context, List) (attr.Value, diag.Diagnostics) + ValueFromList(context.Context, List) (ListValuable, diag.Diagnostics) } +// ListValuable extends attr.Value for list value types. +// Implement this interface to create a custom List value type. type ListValuable interface { attr.Value @@ -178,7 +182,7 @@ func (l ListType) ValueType(_ context.Context) attr.Value { } // ValueFromList returns an attr.Value given a List. -func (l ListType) ValueFromList(_ context.Context, list List) (attr.Value, diag.Diagnostics) { +func (l ListType) ValueFromList(_ context.Context, list List) (ListValuable, diag.Diagnostics) { return list, nil } diff --git a/types/map.go b/types/map.go index 95df88628..cda1df6a8 100644 --- a/types/map.go +++ b/types/map.go @@ -20,12 +20,16 @@ var ( _ MapValuable = &Map{} ) +// MapTypable extends attr.Type for map type types. +// Implement this interface to create a custom MapType type type. type MapTypable interface { attr.Type - ValueFromMap(context.Context, Map) (attr.Value, diag.Diagnostics) + ValueFromMap(context.Context, Map) (MapValuable, diag.Diagnostics) } +// MapValuable extends attr.Value for map value types. +// Implement this interface to create a custom Map value type. type MapValuable interface { attr.Value @@ -182,7 +186,7 @@ func (m MapType) ValueType(_ context.Context) attr.Value { } // ValueFromMap returns an attr.Value given a Map. -func (m MapType) ValueFromMap(_ context.Context, ma Map) (attr.Value, diag.Diagnostics) { +func (m MapType) ValueFromMap(_ context.Context, ma Map) (MapValuable, diag.Diagnostics) { return ma, nil } diff --git a/types/number.go b/types/number.go index 091fde063..3db58602b 100644 --- a/types/number.go +++ b/types/number.go @@ -15,6 +15,8 @@ var ( _ NumberValuable = Number{} ) +// NumberValuable extends attr.Value for number value types. +// Implement this interface to create a custom Number value type. type NumberValuable interface { attr.Value diff --git a/types/object.go b/types/object.go index 68d521713..552b696d2 100644 --- a/types/object.go +++ b/types/object.go @@ -19,12 +19,16 @@ var ( _ ObjectValuable = &Object{} ) +// ObjectTypable extends attr.Type for object type types. +// Implement this interface to create a custom ObjectType type type. type ObjectTypable interface { attr.Type - ValueFromObject(context.Context, Object) (attr.Value, diag.Diagnostics) + ValueFromObject(context.Context, Object) (ObjectValuable, diag.Diagnostics) } +// ObjectValuable extends attr.Value for object value types. +// Implement this interface to create a custom Object value type. type ObjectValuable interface { attr.Value @@ -160,7 +164,7 @@ func (o ObjectType) ValueType(_ context.Context) attr.Value { } // ValueFromObject returns an attr.Value given an Object. -func (o ObjectType) ValueFromObject(_ context.Context, obj Object) (attr.Value, diag.Diagnostics) { +func (o ObjectType) ValueFromObject(_ context.Context, obj Object) (ObjectValuable, diag.Diagnostics) { return obj, nil } diff --git a/types/primitive.go b/types/primitive.go index fff5b6ddc..43cb08608 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -39,58 +39,68 @@ var ( _ Float64Typable = Float64Type ) +// StringTypable extends attr.Type for string type types. +// Implement this interface to create a custom StringType type type. type StringTypable interface { attr.Type - ValueFromString(context.Context, String) (attr.Value, diag.Diagnostics) + ValueFromString(context.Context, String) (StringValuable, diag.Diagnostics) } +// NumberTypable extends attr.Type for number type types. +// Implement this interface to create a custom NumberType type type. type NumberTypable interface { attr.Type - ValueFromNumber(context.Context, Number) (attr.Value, diag.Diagnostics) + ValueFromNumber(context.Context, Number) (NumberValuable, diag.Diagnostics) } +// BoolTypable extends attr.Type for bool type types. +// Implement this interface to create a custom BoolType type type. type BoolTypable interface { attr.Type - ValueFromBool(context.Context, Bool) (attr.Value, diag.Diagnostics) + ValueFromBool(context.Context, Bool) (BoolValuable, diag.Diagnostics) } +// Int64Typable extends attr.Type for int64 type types. +// Implement this interface to create a custom Int64Type type type. type Int64Typable interface { xattr.TypeWithValidate - ValueFromInt64(context.Context, Int64) (attr.Value, diag.Diagnostics) + ValueFromInt64(context.Context, Int64) (Int64Valuable, diag.Diagnostics) } +// Float64Typable extends attr.Type for float64 type types. +// Implement this interface to create a custom Float64Type type type. type Float64Typable interface { xattr.TypeWithValidate - ValueFromFloat64(context.Context, Float64) (attr.Value, diag.Diagnostics) + ValueFromFloat64(context.Context, Float64) (Float64Valuable, diag.Diagnostics) } // ValueFromString returns an attr.Value given a String. -func (p primitive) ValueFromString(_ context.Context, s String) (attr.Value, diag.Diagnostics) { +func (p primitive) ValueFromString(_ context.Context, s String) (StringValuable, diag.Diagnostics) { return s, nil } // ValueFromNumber returns an attr.Value given a Number. -func (p primitive) ValueFromNumber(_ context.Context, n Number) (attr.Value, diag.Diagnostics) { +func (p primitive) ValueFromNumber(_ context.Context, n Number) (NumberValuable, diag.Diagnostics) { return n, nil } // ValueFromBool returns an attr.Value given a Bool. -func (p primitive) ValueFromBool(_ context.Context, b Bool) (attr.Value, diag.Diagnostics) { +func (p primitive) ValueFromBool(_ context.Context, b Bool) (BoolValuable, diag.Diagnostics) { return b, nil } // ValueFromInt64 returns an attr.Value given a Int64. -func (p primitive) ValueFromInt64(_ context.Context, i Int64) (attr.Value, diag.Diagnostics) { +func (p primitive) ValueFromInt64(_ context.Context, i Int64) (Int64Valuable, diag.Diagnostics) { return i, nil } // ValueFromFloat64 returns an attr.Value given a Float64. -func (p primitive) ValueFromFloat64(_ context.Context, f Float64) (attr.Value, diag.Diagnostics) { +func (p primitive) ValueFromFloat64(_ context.Context, f Float64) (Float64Valuable, diag.Diagnostics) { return f, nil } diff --git a/types/set.go b/types/set.go index 84ffc2990..910985b60 100644 --- a/types/set.go +++ b/types/set.go @@ -20,12 +20,16 @@ var ( _ SetValuable = &Set{} ) +// SetTypable extends attr.Type for set type types. +// Implement this interface to create a custom SetType type type. type SetTypable interface { attr.Type - ValueFromSet(context.Context, Set) (attr.Value, diag.Diagnostics) + ValueFromSet(context.Context, Set) (SetValuable, diag.Diagnostics) } +// SetValuable extends attr.Value for set value types. +// Implement this interface to create a custom Set value type. type SetValuable interface { attr.Value @@ -210,7 +214,7 @@ func (st SetType) ValueType(_ context.Context) attr.Value { } // ValueFromSet returns an attr.Value given a Set. -func (st SetType) ValueFromSet(_ context.Context, set Set) (attr.Value, diag.Diagnostics) { +func (st SetType) ValueFromSet(_ context.Context, set Set) (SetValuable, diag.Diagnostics) { return set, nil } diff --git a/types/string.go b/types/string.go index a528f84e6..3ec435ad3 100644 --- a/types/string.go +++ b/types/string.go @@ -14,6 +14,8 @@ var ( _ StringValuable = String{} ) +// StringValuable extends attr.Value for string value types. +// Implement this interface to create a custom String value type. type StringValuable interface { attr.Value From 59d7778d570067a356df688b99be7ef82b403e7e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 14 Nov 2022 11:19:38 +0000 Subject: [PATCH 08/11] Adding further Go documentation (#535) --- types/bool.go | 1 + types/float64.go | 1 + types/int64.go | 1 + types/list.go | 2 ++ types/map.go | 2 ++ types/number.go | 1 + types/object.go | 2 ++ types/primitive.go | 5 +++++ types/set.go | 2 ++ types/string.go | 1 + 10 files changed, 18 insertions(+) diff --git a/types/bool.go b/types/bool.go index 1e92b34f0..9756eb035 100644 --- a/types/bool.go +++ b/types/bool.go @@ -19,6 +19,7 @@ var ( type BoolValuable interface { attr.Value + // ToBoolValue should convert the value type to a Bool. ToBoolValue(ctx context.Context) (Bool, diag.Diagnostics) } diff --git a/types/float64.go b/types/float64.go index a3b229c2d..cb5993a7a 100644 --- a/types/float64.go +++ b/types/float64.go @@ -21,6 +21,7 @@ var ( type Float64Valuable interface { attr.Value + // ToFloat64Value should convert the value type to a Float64. ToFloat64Value(ctx context.Context) (Float64, diag.Diagnostics) } diff --git a/types/int64.go b/types/int64.go index 7e152c472..bae5a6e87 100644 --- a/types/int64.go +++ b/types/int64.go @@ -21,6 +21,7 @@ var ( type Int64Valuable interface { attr.Value + // ToInt64Value should convert the value type to an Int64. ToInt64Value(ctx context.Context) (Int64, diag.Diagnostics) } diff --git a/types/list.go b/types/list.go index 307ca2d31..709dfb925 100644 --- a/types/list.go +++ b/types/list.go @@ -24,6 +24,7 @@ var ( type ListTypable interface { attr.Type + // ValueFromList should convert the List to a ListValuable type. ValueFromList(context.Context, List) (ListValuable, diag.Diagnostics) } @@ -32,6 +33,7 @@ type ListTypable interface { type ListValuable interface { attr.Value + // ToListValue should convert the value type to a List. ToListValue(ctx context.Context) (List, diag.Diagnostics) } diff --git a/types/map.go b/types/map.go index cda1df6a8..d7b4d9a64 100644 --- a/types/map.go +++ b/types/map.go @@ -25,6 +25,7 @@ var ( type MapTypable interface { attr.Type + // ValueFromMap should convert the Map to a MapValuable type. ValueFromMap(context.Context, Map) (MapValuable, diag.Diagnostics) } @@ -33,6 +34,7 @@ type MapTypable interface { type MapValuable interface { attr.Value + // ToMapValue should convert the value type to a Map. ToMapValue(ctx context.Context) (Map, diag.Diagnostics) } diff --git a/types/number.go b/types/number.go index 3db58602b..bfaba3a6d 100644 --- a/types/number.go +++ b/types/number.go @@ -20,6 +20,7 @@ var ( type NumberValuable interface { attr.Value + // ToNumberValue should convert the value type to a Number. ToNumberValue(ctx context.Context) (Number, diag.Diagnostics) } diff --git a/types/object.go b/types/object.go index 552b696d2..9b64c2f84 100644 --- a/types/object.go +++ b/types/object.go @@ -24,6 +24,7 @@ var ( type ObjectTypable interface { attr.Type + // ValueFromObject should convert the Object to an ObjectValuable type. ValueFromObject(context.Context, Object) (ObjectValuable, diag.Diagnostics) } @@ -32,6 +33,7 @@ type ObjectTypable interface { type ObjectValuable interface { attr.Value + // ToObjectValue should convert the value type to an Object. ToObjectValue(ctx context.Context) (Object, diag.Diagnostics) } diff --git a/types/primitive.go b/types/primitive.go index 43cb08608..810e25d78 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -44,6 +44,7 @@ var ( type StringTypable interface { attr.Type + // ValueFromString should convert the String to a StringValuable type. ValueFromString(context.Context, String) (StringValuable, diag.Diagnostics) } @@ -52,6 +53,7 @@ type StringTypable interface { type NumberTypable interface { attr.Type + // ValueFromNumber should convert the Number to a NumberValuable type. ValueFromNumber(context.Context, Number) (NumberValuable, diag.Diagnostics) } @@ -60,6 +62,7 @@ type NumberTypable interface { type BoolTypable interface { attr.Type + // ValueFromBool should convert the Bool to a BoolValuable type. ValueFromBool(context.Context, Bool) (BoolValuable, diag.Diagnostics) } @@ -68,6 +71,7 @@ type BoolTypable interface { type Int64Typable interface { xattr.TypeWithValidate + // ValueFromInt64 should convert the Int64 to a Int64Valuable type. ValueFromInt64(context.Context, Int64) (Int64Valuable, diag.Diagnostics) } @@ -76,6 +80,7 @@ type Int64Typable interface { type Float64Typable interface { xattr.TypeWithValidate + // ValueFromFloat64 should convert the Float64 to a Float64Valuable type. ValueFromFloat64(context.Context, Float64) (Float64Valuable, diag.Diagnostics) } diff --git a/types/set.go b/types/set.go index 910985b60..580b3781d 100644 --- a/types/set.go +++ b/types/set.go @@ -25,6 +25,7 @@ var ( type SetTypable interface { attr.Type + // ValueFromSet should convert the Set to a SetValuable type. ValueFromSet(context.Context, Set) (SetValuable, diag.Diagnostics) } @@ -33,6 +34,7 @@ type SetTypable interface { type SetValuable interface { attr.Value + // ToSetValue should convert the value type to a Set. ToSetValue(ctx context.Context) (Set, diag.Diagnostics) } diff --git a/types/string.go b/types/string.go index 3ec435ad3..e99679a92 100644 --- a/types/string.go +++ b/types/string.go @@ -19,6 +19,7 @@ var ( type StringValuable interface { attr.Value + // ToStringValue should convert the value type to a String. ToStringValue(ctx context.Context) (String, diag.Diagnostics) } From c14cc921851b1015e604c05d02c5c1fd9184eade Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 14 Nov 2022 11:25:25 +0000 Subject: [PATCH 09/11] Apply suggestions from code review Co-authored-by: Brian Flad --- types/bool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/bool.go b/types/bool.go index 9756eb035..ab5cee6a1 100644 --- a/types/bool.go +++ b/types/bool.go @@ -16,9 +16,12 @@ var ( // BoolValuable extends attr.Value for bool value types. // Implement this interface to create a custom Bool value type. +// BoolValuable extends attr.Value for boolean value types. +// Implement this interface to create a custom Bool value type. type BoolValuable interface { attr.Value + // ToBoolValue should convert the value type to a Bool. // ToBoolValue should convert the value type to a Bool. ToBoolValue(ctx context.Context) (Bool, diag.Diagnostics) } From e117313deb9e96e270aacdf589f94eb14afa8b9e Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 14 Nov 2022 11:35:17 +0000 Subject: [PATCH 10/11] Amending Go documentation (#535) --- types/bool.go | 3 --- types/list.go | 2 +- types/map.go | 2 +- types/object.go | 2 +- types/primitive.go | 10 +++++----- types/set.go | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/types/bool.go b/types/bool.go index ab5cee6a1..2b4bd5236 100644 --- a/types/bool.go +++ b/types/bool.go @@ -14,14 +14,11 @@ var ( _ BoolValuable = Bool{} ) -// BoolValuable extends attr.Value for bool value types. -// Implement this interface to create a custom Bool value type. // BoolValuable extends attr.Value for boolean value types. // Implement this interface to create a custom Bool value type. type BoolValuable interface { attr.Value - // ToBoolValue should convert the value type to a Bool. // ToBoolValue should convert the value type to a Bool. ToBoolValue(ctx context.Context) (Bool, diag.Diagnostics) } diff --git a/types/list.go b/types/list.go index 709dfb925..781dcba59 100644 --- a/types/list.go +++ b/types/list.go @@ -183,7 +183,7 @@ func (l ListType) ValueType(_ context.Context) attr.Value { } } -// ValueFromList returns an attr.Value given a List. +// ValueFromList returns a ListValuable type given a List. func (l ListType) ValueFromList(_ context.Context, list List) (ListValuable, diag.Diagnostics) { return list, nil } diff --git a/types/map.go b/types/map.go index d7b4d9a64..e63c928cb 100644 --- a/types/map.go +++ b/types/map.go @@ -187,7 +187,7 @@ func (m MapType) ValueType(_ context.Context) attr.Value { } } -// ValueFromMap returns an attr.Value given a Map. +// ValueFromMap returns a MapValuable type given a Map. func (m MapType) ValueFromMap(_ context.Context, ma Map) (MapValuable, diag.Diagnostics) { return ma, nil } diff --git a/types/object.go b/types/object.go index 9b64c2f84..25ce45c5d 100644 --- a/types/object.go +++ b/types/object.go @@ -165,7 +165,7 @@ func (o ObjectType) ValueType(_ context.Context) attr.Value { } } -// ValueFromObject returns an attr.Value given an Object. +// ValueFromObject returns an ObjectValuable type given an Object. func (o ObjectType) ValueFromObject(_ context.Context, obj Object) (ObjectValuable, diag.Diagnostics) { return obj, nil } diff --git a/types/primitive.go b/types/primitive.go index 810e25d78..5db6b13a3 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -84,27 +84,27 @@ type Float64Typable interface { ValueFromFloat64(context.Context, Float64) (Float64Valuable, diag.Diagnostics) } -// ValueFromString returns an attr.Value given a String. +// ValueFromString returns a StringValuable type given a String. func (p primitive) ValueFromString(_ context.Context, s String) (StringValuable, diag.Diagnostics) { return s, nil } -// ValueFromNumber returns an attr.Value given a Number. +// ValueFromNumber returns a NumberValuable type given a Number. func (p primitive) ValueFromNumber(_ context.Context, n Number) (NumberValuable, diag.Diagnostics) { return n, nil } -// ValueFromBool returns an attr.Value given a Bool. +// ValueFromBool returns a BoolValuable type given a Bool. func (p primitive) ValueFromBool(_ context.Context, b Bool) (BoolValuable, diag.Diagnostics) { return b, nil } -// ValueFromInt64 returns an attr.Value given a Int64. +// ValueFromInt64 returns an Int64Valuable type given an Int64. func (p primitive) ValueFromInt64(_ context.Context, i Int64) (Int64Valuable, diag.Diagnostics) { return i, nil } -// ValueFromFloat64 returns an attr.Value given a Float64. +// ValueFromFloat64 returns a Float64Valuable type given a Float64. func (p primitive) ValueFromFloat64(_ context.Context, f Float64) (Float64Valuable, diag.Diagnostics) { return f, nil } diff --git a/types/set.go b/types/set.go index 580b3781d..1a1240643 100644 --- a/types/set.go +++ b/types/set.go @@ -215,7 +215,7 @@ func (st SetType) ValueType(_ context.Context) attr.Value { } } -// ValueFromSet returns an attr.Value given a Set. +// ValueFromSet returns a SetValuable type given a Set. func (st SetType) ValueFromSet(_ context.Context, set Set) (SetValuable, diag.Diagnostics) { return set, nil } From a54818087d67234d503f6c28799474a1474e4861 Mon Sep 17 00:00:00 2001 From: Benjamin Bennett Date: Mon, 14 Nov 2022 17:50:00 +0000 Subject: [PATCH 11/11] Adding changelog, amending Go documentation and adding unit tests for attribute plan modification and attribute validation for custom nested attribute types (#535) --- .changelog/536.txt | 35 ++ .../attribute_plan_modification_test.go | 356 ++++++++++++- internal/fwserver/attribute_validation.go | 8 +- .../fwserver/attribute_validation_test.go | 473 ++++++++++++++++++ internal/fwserver/block_validation.go | 6 +- .../types/listnestedattributescustom.go | 51 ++ .../types/mapnestedattributescustom.go | 51 ++ .../types/setnestedattributescustom.go | 51 ++ .../types/singlenestedattributescustom.go | 51 ++ types/list.go | 4 +- types/map.go | 4 +- types/object.go | 4 +- types/primitive.go | 20 +- types/set.go | 4 +- 14 files changed, 1092 insertions(+), 26 deletions(-) create mode 100644 .changelog/536.txt create mode 100644 internal/testing/types/listnestedattributescustom.go create mode 100644 internal/testing/types/mapnestedattributescustom.go create mode 100644 internal/testing/types/setnestedattributescustom.go create mode 100644 internal/testing/types/singlenestedattributescustom.go diff --git a/.changelog/536.txt b/.changelog/536.txt new file mode 100644 index 000000000..468439c0b --- /dev/null +++ b/.changelog/536.txt @@ -0,0 +1,35 @@ +```release-note:enhancement +types: Added `BoolTypable` and `BoolValuable` interface types, which enable embedding existing boolean types for custom types +``` + +```release-note:enhancement +types: Added `Float64Typable` and `Float64Valuable` interface types, which enable embedding existing float64 types for custom types +``` + +```release-note:enhancement +types: Added `Int64Typable` and `Int64Valuable` interface types, which enable embedding existing int64 types for custom types +``` + +```release-note:enhancement +types: Added `ListTypable` and `ListValuable` interface types, which enable embedding existing list types for custom types +``` + +```release-note:enhancement +types: Added `MapTypable` and `MapValuable` interface types, which enable embedding existing map types for custom types +``` + +```release-note:enhancement +types: Added `NumberTypable` and `NumberValuable` interface types, which enable embedding existing number types for custom types +``` + +```release-note:enhancement +types: Added `ObjectTypable` and `ObjectValuable` interface types, which enable embedding existing object types for custom types +``` + +```release-note:enhancement +types: Added `SetTypable` and `SetValuable` interface types, which enable embedding existing set types for custom types +``` + +```release-note:enhancement +types: Added `StringTypable` and `StringValuable` interface types, which enable embedding existing string types for custom types +``` diff --git a/internal/fwserver/attribute_plan_modification_test.go b/internal/fwserver/attribute_plan_modification_test.go index e5a280517..8ca0a0fae 100644 --- a/internal/fwserver/attribute_plan_modification_test.go +++ b/internal/fwserver/attribute_plan_modification_test.go @@ -6,16 +6,18 @@ import ( "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/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" "github.com/hashicorp/terraform-plugin-framework/internal/privatestate" "github.com/hashicorp/terraform-plugin-framework/internal/testing/planmodifiers" + testtypes "github.com/hashicorp/terraform-plugin-framework/internal/testing/types" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" ) func TestAttributeModifyPlan(t *testing.T) { @@ -199,6 +201,103 @@ func TestAttributeModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "attribute-list-nested-custom": { + attribute: tfsdk.Attribute{ + Attributes: testtypes.ListNestedAttributesCustomType{ + NestedAttributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }), + }, + Required: true, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: testtypes.ListNestedAttributesCustomValue{ + List: types.ListValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributePath: path.Root("test"), + AttributePlan: testtypes.ListNestedAttributesCustomValue{ + List: types.ListValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributeState: testtypes.ListNestedAttributesCustomValue{ + List: types.ListValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.ListValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("MODIFIED_TWO"), + }, + ), + }, + ), + Private: testEmptyProviderData, + }, + }, "attribute-set-nested-private": { attribute: tfsdk.Attribute{ Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ @@ -290,6 +389,103 @@ func TestAttributeModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "attribute-custom-set-nested": { + attribute: tfsdk.Attribute{ + Attributes: testtypes.SetNestedAttributesCustomType{ + NestedAttributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }), + }, + Required: true, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: testtypes.SetNestedAttributesCustomValue{ + Set: types.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributePath: path.Root("test"), + AttributePlan: testtypes.SetNestedAttributesCustomValue{ + Set: types.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributeState: testtypes.SetNestedAttributesCustomValue{ + Set: types.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.SetValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + []attr.Value{ + types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("MODIFIED_TWO"), + }, + ), + }, + ), + Private: testEmptyProviderData, + }, + }, "attribute-set-nested-usestateforunknown": { attribute: tfsdk.Attribute{ Attributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ @@ -525,6 +721,103 @@ func TestAttributeModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "attribute-custom-map-nested": { + attribute: tfsdk.Attribute{ + Attributes: testtypes.MapNestedAttributesCustomType{ + NestedAttributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }), + }, + Required: true, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: testtypes.MapNestedAttributesCustomValue{ + Map: types.MapValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + map[string]attr.Value{ + "testkey": types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributePath: path.Root("test"), + AttributePlan: testtypes.MapNestedAttributesCustomValue{ + Map: types.MapValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + map[string]attr.Value{ + "testkey": types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + AttributeState: testtypes.MapNestedAttributesCustomValue{ + Map: types.MapValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + map[string]attr.Value{ + "testkey": types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("TESTATTRONE"), + }, + ), + }, + ), + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.MapValueMust( + types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "nested_attr": types.StringType, + }, + }, + map[string]attr.Value{ + "testkey": types.ObjectValueMust( + map[string]attr.Type{ + "nested_attr": types.StringType, + }, + map[string]attr.Value{ + "nested_attr": types.StringValue("MODIFIED_TWO"), + }, + ), + }, + ), + Private: testEmptyProviderData, + }, + }, "attribute-single-nested-private": { attribute: tfsdk.Attribute{ Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ @@ -580,6 +873,67 @@ func TestAttributeModifyPlan(t *testing.T) { Private: testProviderData, }, }, + "attribute-custom-single-nested": { + attribute: tfsdk.Attribute{ + Attributes: testtypes.SingleNestedAttributesCustomType{ + NestedAttributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "testing": { + Type: types.StringType, + Optional: true, + PlanModifiers: tfsdk.AttributePlanModifiers{ + planmodifiers.TestAttrPlanValueModifierOne{}, + planmodifiers.TestAttrPlanValueModifierTwo{}, + }, + }, + }), + }, + Required: true, + }, + req: tfsdk.ModifyAttributePlanRequest{ + AttributeConfig: testtypes.SingleNestedAttributesCustomValue{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "testing": types.StringType, + }, + map[string]attr.Value{ + "testing": types.StringValue("TESTATTRONE"), + }, + ), + }, + AttributePath: path.Root("test"), + AttributePlan: testtypes.SingleNestedAttributesCustomValue{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "testing": types.StringType, + }, + map[string]attr.Value{ + "testing": types.StringValue("TESTATTRONE"), + }, + ), + }, + AttributeState: testtypes.SingleNestedAttributesCustomValue{ + Object: types.ObjectValueMust( + map[string]attr.Type{ + "testing": types.StringType, + }, + map[string]attr.Value{ + "testing": types.StringValue("TESTATTRONE"), + }, + ), + }, + }, + expectedResp: ModifyAttributePlanResponse{ + AttributePlan: types.ObjectValueMust( + map[string]attr.Type{ + "testing": types.StringType, + }, + map[string]attr.Value{ + "testing": types.StringValue("MODIFIED_TWO"), + }, + ), + Private: testEmptyProviderData, + }, + }, "requires-replacement": { attribute: tfsdk.Attribute{ Type: types.StringType, diff --git a/internal/fwserver/attribute_validation.go b/internal/fwserver/attribute_validation.go index 7483dbf19..8639ea188 100644 --- a/internal/fwserver/attribute_validation.go +++ b/internal/fwserver/attribute_validation.go @@ -147,7 +147,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute resp.Diagnostics.AddAttributeError( req.AttributePath, "Attribute Validation Error Invalid Value Type", - "A type from which a types.List can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.ListValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return @@ -184,7 +184,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute resp.Diagnostics.AddAttributeError( req.AttributePath, "Attribute Validation Error Invalid Value Type", - "A type from which a types.Set can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.SetValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return @@ -221,7 +221,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute resp.Diagnostics.AddAttributeError( req.AttributePath, "Attribute Validation Error Invalid Value Type", - "A type from which a types.Map can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.MapValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return @@ -258,7 +258,7 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute resp.Diagnostics.AddAttributeError( req.AttributePath, "Attribute Validation Error Invalid Value Type", - "A type from which a types.Object can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.ObjectValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return diff --git a/internal/fwserver/attribute_validation_test.go b/internal/fwserver/attribute_validation_test.go index 423080c48..2ad7f03d2 100644 --- a/internal/fwserver/attribute_validation_test.go +++ b/internal/fwserver/attribute_validation_test.go @@ -665,6 +665,65 @@ func TestAttributeValidate(t *testing.T) { }, resp: tfsdk.ValidateAttributeResponse{}, }, + "nested-custom-attr-list-no-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.ListNestedAttributesCustomType{ + NestedAttributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{}, + }, "nested-attr-list-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -729,6 +788,72 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "nested-custom-attr-list-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.List{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.ListNestedAttributesCustomType{ + NestedAttributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + Validators: []tfsdk.AttributeValidator{ + testErrorAttributeValidator{}, + }, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + testErrorDiagnostic1, + }, + }, + }, "nested-attr-map-no-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -786,6 +911,65 @@ func TestAttributeValidate(t *testing.T) { }, resp: tfsdk.ValidateAttributeResponse{}, }, + "nested-custom-attr-map-no-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.MapNestedAttributesCustomType{ + NestedAttributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{}, + }, "nested-attr-map-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -850,6 +1034,72 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "nested-custom-attr-map-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Map{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + map[string]tftypes.Value{ + "testkey": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.MapNestedAttributesCustomType{ + NestedAttributes: tfsdk.MapNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + Validators: []tfsdk.AttributeValidator{ + testErrorAttributeValidator{}, + }, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + testErrorDiagnostic1, + }, + }, + }, "nested-attr-set-no-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -907,6 +1157,65 @@ func TestAttributeValidate(t *testing.T) { }, resp: tfsdk.ValidateAttributeResponse{}, }, + "nested-custom-attr-set-no-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.SetNestedAttributesCustomType{ + NestedAttributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{}, + }, "nested-attr-set-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -971,6 +1280,72 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "nested-custom-attr-set-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Set{ + ElementType: tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.SetNestedAttributesCustomType{ + NestedAttributes: tfsdk.SetNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + Validators: []tfsdk.AttributeValidator{ + testErrorAttributeValidator{}, + }, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + testErrorDiagnostic1, + }, + }, + }, "nested-attr-single-no-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -1015,6 +1390,52 @@ func TestAttributeValidate(t *testing.T) { }, resp: tfsdk.ValidateAttributeResponse{}, }, + "nested-custom-attr-single-no-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, + map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.SingleNestedAttributesCustomType{ + NestedAttributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{}, + }, "nested-attr-single-validation": { req: tfsdk.ValidateAttributeRequest{ AttributePath: path.Root("test"), @@ -1065,6 +1486,58 @@ func TestAttributeValidate(t *testing.T) { }, }, }, + "nested-custom-attr-single-validation": { + req: tfsdk.ValidateAttributeRequest{ + AttributePath: path.Root("test"), + Config: tfsdk.Config{ + Raw: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test": tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + }, + }, map[string]tftypes.Value{ + "test": tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "nested_attr": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "nested_attr": tftypes.NewValue(tftypes.String, "testvalue"), + }, + ), + }, + ), + Schema: tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test": { + Attributes: testtypes.SingleNestedAttributesCustomType{ + NestedAttributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "nested_attr": { + Type: types.StringType, + Required: true, + Validators: []tfsdk.AttributeValidator{ + testErrorAttributeValidator{}, + }, + }, + }), + }, + Required: true, + }, + }, + }, + }, + }, + resp: tfsdk.ValidateAttributeResponse{ + Diagnostics: diag.Diagnostics{ + testErrorDiagnostic1, + }, + }, + }, } for name, tc := range testCases { diff --git a/internal/fwserver/block_validation.go b/internal/fwserver/block_validation.go index f2496d79c..cb5add730 100644 --- a/internal/fwserver/block_validation.go +++ b/internal/fwserver/block_validation.go @@ -56,7 +56,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.AddAttributeError( req.AttributePath, "Block Validation Error Invalid Value Type", - "A type from which a types.List can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.ListValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return @@ -131,7 +131,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.AddAttributeError( req.AttributePath, "Block Validation Error Invalid Value Type", - "A type from which a types.Set can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.SetValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return @@ -206,7 +206,7 @@ func BlockValidate(ctx context.Context, b fwschema.Block, req tfsdk.ValidateAttr resp.Diagnostics.AddAttributeError( req.AttributePath, "Block Validation Error Invalid Value Type", - "A type from which a types.Object can be obtained is expected here. Report this to the provider developer:\n\n"+err.Error(), + "A type that implements types.ObjectValuable is expected here. Report this to the provider developer:\n\n"+err.Error(), ) return diff --git a/internal/testing/types/listnestedattributescustom.go b/internal/testing/types/listnestedattributescustom.go new file mode 100644 index 000000000..91d3178e8 --- /dev/null +++ b/internal/testing/types/listnestedattributescustom.go @@ -0,0 +1,51 @@ +package types + +import ( + "context" + "fmt" + + "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" +) + +var ( + _ types.ListTypable = ListNestedAttributesCustomTypeType{} + _ types.ListValuable = &ListNestedAttributesCustomValue{} +) + +type ListNestedAttributesCustomType struct { + fwschema.NestedAttributes +} + +func (t ListNestedAttributesCustomType) Type() attr.Type { + return ListNestedAttributesCustomTypeType{ + t.NestedAttributes.Type().(types.ListType), + } +} + +type ListNestedAttributesCustomTypeType struct { + types.ListType +} + +func (tt ListNestedAttributesCustomTypeType) ValueFromTerraform(ctx context.Context, value tftypes.Value) (attr.Value, error) { + val, err := tt.ListType.ValueFromTerraform(ctx, value) + if err != nil { + return nil, err + } + + list, ok := val.(types.List) + if !ok { + return nil, fmt.Errorf("cannot assert %T as types.List", val) + } + + return ListNestedAttributesCustomValue{ + list, + }, nil +} + +type ListNestedAttributesCustomValue struct { + types.List +} diff --git a/internal/testing/types/mapnestedattributescustom.go b/internal/testing/types/mapnestedattributescustom.go new file mode 100644 index 000000000..facef9032 --- /dev/null +++ b/internal/testing/types/mapnestedattributescustom.go @@ -0,0 +1,51 @@ +package types + +import ( + "context" + "fmt" + + "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" +) + +var ( + _ types.MapTypable = MapNestedAttributesCustomTypeType{} + _ types.MapValuable = &MapNestedAttributesCustomValue{} +) + +type MapNestedAttributesCustomType struct { + fwschema.NestedAttributes +} + +func (t MapNestedAttributesCustomType) Type() attr.Type { + return MapNestedAttributesCustomTypeType{ + t.NestedAttributes.Type().(types.MapType), + } +} + +type MapNestedAttributesCustomTypeType struct { + types.MapType +} + +func (tt MapNestedAttributesCustomTypeType) ValueFromTerraform(ctx context.Context, value tftypes.Value) (attr.Value, error) { + val, err := tt.MapType.ValueFromTerraform(ctx, value) + if err != nil { + return nil, err + } + + m, ok := val.(types.Map) + if !ok { + return nil, fmt.Errorf("cannot assert %T as types.Map", val) + } + + return MapNestedAttributesCustomValue{ + m, + }, nil +} + +type MapNestedAttributesCustomValue struct { + types.Map +} diff --git a/internal/testing/types/setnestedattributescustom.go b/internal/testing/types/setnestedattributescustom.go new file mode 100644 index 000000000..5751ffabd --- /dev/null +++ b/internal/testing/types/setnestedattributescustom.go @@ -0,0 +1,51 @@ +package types + +import ( + "context" + "fmt" + + "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" +) + +var ( + _ types.SetTypable = SetNestedAttributesCustomTypeType{} + _ types.SetValuable = &SetNestedAttributesCustomValue{} +) + +type SetNestedAttributesCustomType struct { + fwschema.NestedAttributes +} + +func (t SetNestedAttributesCustomType) Type() attr.Type { + return SetNestedAttributesCustomTypeType{ + t.NestedAttributes.Type().(types.SetType), + } +} + +type SetNestedAttributesCustomTypeType struct { + types.SetType +} + +func (tt SetNestedAttributesCustomTypeType) ValueFromTerraform(ctx context.Context, value tftypes.Value) (attr.Value, error) { + val, err := tt.SetType.ValueFromTerraform(ctx, value) + if err != nil { + return nil, err + } + + s, ok := val.(types.Set) + if !ok { + return nil, fmt.Errorf("cannot assert %T as types.Set", val) + } + + return SetNestedAttributesCustomValue{ + s, + }, nil +} + +type SetNestedAttributesCustomValue struct { + types.Set +} diff --git a/internal/testing/types/singlenestedattributescustom.go b/internal/testing/types/singlenestedattributescustom.go new file mode 100644 index 000000000..ff3654c65 --- /dev/null +++ b/internal/testing/types/singlenestedattributescustom.go @@ -0,0 +1,51 @@ +package types + +import ( + "context" + "fmt" + + "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" +) + +var ( + _ types.SetTypable = SetNestedAttributesCustomTypeType{} + _ types.SetValuable = &SetNestedAttributesCustomValue{} +) + +type SingleNestedAttributesCustomType struct { + fwschema.NestedAttributes +} + +func (t SingleNestedAttributesCustomType) Type() attr.Type { + return SingleNestedAttributesCustomTypeType{ + t.NestedAttributes.Type().(types.ObjectType), + } +} + +type SingleNestedAttributesCustomTypeType struct { + types.ObjectType +} + +func (tt SingleNestedAttributesCustomTypeType) ValueFromTerraform(ctx context.Context, value tftypes.Value) (attr.Value, error) { + val, err := tt.ObjectType.ValueFromTerraform(ctx, value) + if err != nil { + return nil, err + } + + s, ok := val.(types.Object) + if !ok { + return nil, fmt.Errorf("cannot assert %T as types.Object", val) + } + + return SingleNestedAttributesCustomValue{ + s, + }, nil +} + +type SingleNestedAttributesCustomValue struct { + types.Object +} diff --git a/types/list.go b/types/list.go index 781dcba59..31c40232e 100644 --- a/types/list.go +++ b/types/list.go @@ -19,8 +19,8 @@ var ( _ ListValuable = &List{} ) -// ListTypable extends attr.Type for list type types. -// Implement this interface to create a custom ListType type type. +// ListTypable extends attr.Type for list types. +// Implement this interface to create a custom ListType type. type ListTypable interface { attr.Type diff --git a/types/map.go b/types/map.go index e63c928cb..990407462 100644 --- a/types/map.go +++ b/types/map.go @@ -20,8 +20,8 @@ var ( _ MapValuable = &Map{} ) -// MapTypable extends attr.Type for map type types. -// Implement this interface to create a custom MapType type type. +// MapTypable extends attr.Type for map types. +// Implement this interface to create a custom MapType type. type MapTypable interface { attr.Type diff --git a/types/object.go b/types/object.go index 25ce45c5d..71cd7155b 100644 --- a/types/object.go +++ b/types/object.go @@ -19,8 +19,8 @@ var ( _ ObjectValuable = &Object{} ) -// ObjectTypable extends attr.Type for object type types. -// Implement this interface to create a custom ObjectType type type. +// ObjectTypable extends attr.Type for object types. +// Implement this interface to create a custom ObjectType type. type ObjectTypable interface { attr.Type diff --git a/types/primitive.go b/types/primitive.go index 5db6b13a3..7f917f396 100644 --- a/types/primitive.go +++ b/types/primitive.go @@ -39,8 +39,8 @@ var ( _ Float64Typable = Float64Type ) -// StringTypable extends attr.Type for string type types. -// Implement this interface to create a custom StringType type type. +// StringTypable extends attr.Type for string types. +// Implement this interface to create a custom StringType type. type StringTypable interface { attr.Type @@ -48,8 +48,8 @@ type StringTypable interface { ValueFromString(context.Context, String) (StringValuable, diag.Diagnostics) } -// NumberTypable extends attr.Type for number type types. -// Implement this interface to create a custom NumberType type type. +// NumberTypable extends attr.Type for number types. +// Implement this interface to create a custom NumberType type. type NumberTypable interface { attr.Type @@ -57,8 +57,8 @@ type NumberTypable interface { ValueFromNumber(context.Context, Number) (NumberValuable, diag.Diagnostics) } -// BoolTypable extends attr.Type for bool type types. -// Implement this interface to create a custom BoolType type type. +// BoolTypable extends attr.Type for bool types. +// Implement this interface to create a custom BoolType type. type BoolTypable interface { attr.Type @@ -66,8 +66,8 @@ type BoolTypable interface { ValueFromBool(context.Context, Bool) (BoolValuable, diag.Diagnostics) } -// Int64Typable extends attr.Type for int64 type types. -// Implement this interface to create a custom Int64Type type type. +// Int64Typable extends attr.Type for int64 types. +// Implement this interface to create a custom Int64Type type. type Int64Typable interface { xattr.TypeWithValidate @@ -75,8 +75,8 @@ type Int64Typable interface { ValueFromInt64(context.Context, Int64) (Int64Valuable, diag.Diagnostics) } -// Float64Typable extends attr.Type for float64 type types. -// Implement this interface to create a custom Float64Type type type. +// Float64Typable extends attr.Type for float64 types. +// Implement this interface to create a custom Float64Type type. type Float64Typable interface { xattr.TypeWithValidate diff --git a/types/set.go b/types/set.go index 1a1240643..c135675c7 100644 --- a/types/set.go +++ b/types/set.go @@ -20,8 +20,8 @@ var ( _ SetValuable = &Set{} ) -// SetTypable extends attr.Type for set type types. -// Implement this interface to create a custom SetType type type. +// SetTypable extends attr.Type for set types. +// Implement this interface to create a custom SetType type. type SetTypable interface { attr.Type