Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/BUG FIXES-20230613-133402.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: BUG FIXES
body: 'resource/schema: Prevented `Value Conversion Error` diagnostics for attributes
and blocks implementing both `CustomType` and `PlanModifiers` fields'
time: 2023-06-13T13:34:02.465635-04:00
custom:
Issue: "754"
147 changes: 147 additions & 0 deletions internal/fwserver/attr_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package fwserver

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

func coerceBoolTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.BoolValuable) (basetypes.BoolTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.BoolTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceFloat64Typable(ctx context.Context, schemaPath path.Path, valuable basetypes.Float64Valuable) (basetypes.Float64Typable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.Float64Typable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceInt64Typable(ctx context.Context, schemaPath path.Path, valuable basetypes.Int64Valuable) (basetypes.Int64Typable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.Int64Typable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceListTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.ListValuable) (basetypes.ListTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.ListTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceMapTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.MapValuable) (basetypes.MapTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.MapTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceNumberTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.NumberValuable) (basetypes.NumberTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.NumberTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceObjectTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.ObjectValuable) (basetypes.ObjectTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.ObjectTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceSetTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.SetValuable) (basetypes.SetTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.SetTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}

func coerceStringTypable(ctx context.Context, schemaPath path.Path, valuable basetypes.StringValuable) (basetypes.StringTypable, diag.Diagnostics) {
typable, ok := valuable.Type(ctx).(basetypes.StringTypable)

// Type() of a Valuable should always be a Typable to recreate the Valuable,
// but if for some reason it is not, raise an implementation error instead
// of a panic.
if !ok {
return nil, diag.Diagnostics{
attributePlanModificationTypableError(schemaPath, valuable),
}
}

return typable, nil
}
84 changes: 72 additions & 12 deletions internal/fwserver/attr_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,112 @@ import (
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.List, diag.Diagnostics) {
listVal, ok := value.(basetypes.ListValuable)
func coerceListValuable(_ context.Context, schemaPath path.Path, value attr.Value) (basetypes.ListValuable, diag.Diagnostics) {
listValuable, ok := value.(basetypes.ListValuable)

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

return listVal.ToListValue(ctx)
return listValuable, nil
}

func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) {
mapVal, ok := value.(basetypes.MapValuable)
func coerceListValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.List, diag.Diagnostics) {
listValuable, diags := coerceListValuable(ctx, schemaPath, value)

if diags.HasError() {
return types.ListNull(nil), diags
}

listValue, listValueDiags := listValuable.ToListValue(ctx)

// Ensure prior warnings are preserved.
diags.Append(listValueDiags...)

return listValue, diags
}

func coerceMapValuable(_ context.Context, schemaPath path.Path, value attr.Value) (basetypes.MapValuable, diag.Diagnostics) {
mapValuable, ok := value.(basetypes.MapValuable)

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

return mapVal.ToMapValue(ctx)
return mapValuable, nil
}

func coerceObjectValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) {
objectVal, ok := value.(basetypes.ObjectValuable)
func coerceMapValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Map, diag.Diagnostics) {
mapValuable, diags := coerceMapValuable(ctx, schemaPath, value)

if diags.HasError() {
return types.MapNull(nil), diags
}

mapValue, mapValueDiags := mapValuable.ToMapValue(ctx)

// Ensure prior warnings are preserved.
diags.Append(mapValueDiags...)

return mapValue, diags
}

func coerceObjectValuable(_ context.Context, schemaPath path.Path, value attr.Value) (basetypes.ObjectValuable, diag.Diagnostics) {
objectValuable, ok := value.(basetypes.ObjectValuable)

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

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

func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) {
setVal, ok := value.(basetypes.SetValuable)
func coerceObjectValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Object, diag.Diagnostics) {
objectValuable, diags := coerceObjectValuable(ctx, schemaPath, value)

if diags.HasError() {
return types.ObjectNull(nil), diags
}

objectValue, objectValueDiags := objectValuable.ToObjectValue(ctx)

// Ensure prior warnings are preserved.
diags.Append(objectValueDiags...)

return objectValue, diags
}

func coerceSetValuable(_ context.Context, schemaPath path.Path, value attr.Value) (basetypes.SetValuable, diag.Diagnostics) {
setValuable, ok := value.(basetypes.SetValuable)

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

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

func coerceSetValue(ctx context.Context, schemaPath path.Path, value attr.Value) (types.Set, diag.Diagnostics) {
setValuable, diags := coerceSetValuable(ctx, schemaPath, value)

if diags.HasError() {
return types.SetNull(nil), diags
}

setValue, setValueDiags := setValuable.ToSetValue(ctx)

// Ensure prior warnings are preserved.
diags.Append(setValueDiags...)

return setValue, diags
}

func listElemObject(ctx context.Context, schemaPath path.Path, list types.List, index int, description fwschemadata.DataDescription) (types.Object, diag.Diagnostics) {
Expand Down
Loading