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 +}