Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: Add Typable and Valuable Interfaces #536

Merged
merged 12 commits into from Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 20 additions & 19 deletions internal/fwserver/attr_value.go
Expand Up @@ -3,60 +3,61 @@ 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.ListValuable)

if !ok {
return types.ListNull(nil), diag.Diagnostics{
attributePlanModificationWalkError(schemaPath, value),
}
}

return list, nil
return listVal.ToListValue(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.MapValuable)

if !ok {
return types.MapNull(nil), diag.Diagnostics{
attributePlanModificationWalkError(schemaPath, value),
}
}

return m, nil
return mapVal.ToMapValue(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.ObjectValuable)

if !ok {
return types.ObjectNull(nil), diag.Diagnostics{
attributePlanModificationWalkError(schemaPath, value),
}
}

return object, nil
return objectVal.ToObjectValue(ctx)
}

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.SetValuable)

if !ok {
return types.SetNull(nil), diag.Diagnostics{
attributePlanModificationWalkError(schemaPath, value),
}
}

return set, nil
return setVal.ToSetValue(ctx)
}

func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) {
Expand All @@ -72,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) {
Expand All @@ -85,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) {
Expand All @@ -103,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) {
Expand All @@ -116,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) {
Expand Down Expand Up @@ -162,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) {
Expand All @@ -175,5 +176,5 @@ func setElemObjectFromTerraformValue(ctx context.Context, schemaPath path.Path,
}
}

return coerceObjectValue(schemaPath, elemValue)
return coerceObjectValue(ctx, schemaPath, elemValue)
}
30 changes: 15 additions & 15 deletions internal/fwserver/attribute_plan_modification.go
Expand Up @@ -98,23 +98,23 @@ 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...)

if resp.Diagnostics.HasError() {
return
}

planList, diags := coerceListValue(req.AttributePath, req.AttributePlan)
planList, diags := coerceListValue(ctx, req.AttributePath, req.AttributePlan)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

stateList, diags := coerceListValue(req.AttributePath, req.AttributeState)
stateList, diags := coerceListValue(ctx, req.AttributePath, req.AttributeState)

resp.Diagnostics.Append(diags...)

Expand All @@ -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...)

Expand Down Expand Up @@ -220,23 +220,23 @@ 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...)

if resp.Diagnostics.HasError() {
return
}

planSet, diags := coerceSetValue(req.AttributePath, req.AttributePlan)
planSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributePlan)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

stateSet, diags := coerceSetValue(req.AttributePath, req.AttributeState)
stateSet, diags := coerceSetValue(ctx, req.AttributePath, req.AttributeState)

resp.Diagnostics.Append(diags...)

Expand All @@ -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...)

Expand Down Expand Up @@ -342,23 +342,23 @@ 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...)

if resp.Diagnostics.HasError() {
return
}

planMap, diags := coerceMapValue(req.AttributePath, req.AttributePlan)
planMap, diags := coerceMapValue(ctx, req.AttributePath, req.AttributePlan)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

stateMap, diags := coerceMapValue(req.AttributePath, req.AttributeState)
stateMap, diags := coerceMapValue(ctx, req.AttributePath, req.AttributeState)

resp.Diagnostics.Append(diags...)

Expand All @@ -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...)

Expand Down Expand Up @@ -464,23 +464,23 @@ 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...)

if resp.Diagnostics.HasError() {
return
}

planObject, diags := coerceObjectValue(req.AttributePath, req.AttributePlan)
planObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributePlan)

resp.Diagnostics.Append(diags...)

if resp.Diagnostics.HasError() {
return
}

stateObject, diags := coerceObjectValue(req.AttributePath, req.AttributeState)
stateObject, diags := coerceObjectValue(ctx, req.AttributePath, req.AttributeState)

resp.Diagnostics.Append(diags...)

Expand Down
52 changes: 40 additions & 12 deletions internal/fwserver/attribute_validation.go
Expand Up @@ -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.ListValuable)

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(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Should we give the direct hint about the types.ListValuable interface for these?

)

return
}

l, diags := listVal.ToListValue(ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're ready for unit testing these! Maybe some additional types in internal/testing/types?


resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

for idx := range l.Elements() {
for nestedName, nestedAttr := range a.GetAttributes().GetAttributes() {
nestedAttrReq := tfsdk.ValidateAttributeRequest{
Expand All @@ -170,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.SetValuable)

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.ToSetValue(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{
Expand All @@ -200,19 +214,26 @@ func AttributeValidateNestedAttributes(ctx context.Context, a fwschema.Attribute
}
}
case fwschema.NestingModeMap:
m, ok := req.AttributeConfig.(types.Map)
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)
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.ToMapValue(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{
Expand All @@ -230,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.ObjectValuable)

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.ToObjectValue(ctx)

resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

if o.IsNull() || o.IsUnknown() {
return
}
Expand Down