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

attr: Add ValueType method to Type interface #497

Merged
merged 3 commits into from Sep 23, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/497.txt
@@ -0,0 +1,7 @@
```release-note:breaking-change
attr: The `Type` interface now requires the `ValueType` method, which is used for enhancing error diagnostics from the framework
```

```release-note:enhancement
internal/reflect: Added `attr.Value` type suggestions to error diagnostics
```
6 changes: 6 additions & 0 deletions attr/type.go
Expand Up @@ -25,6 +25,12 @@ type Type interface {
// for the provider to consume the data with.
ValueFromTerraform(context.Context, tftypes.Value) (Value, error)

// ValueType should return the attr.Value type returned by
// ValueFromTerraform. The returned attr.Value can be any null, unknown,
// or known value for the type, as this is intended for type detection
// and improving error diagnostics.
ValueType(context.Context) Value

// Equal must return true if the Type is considered semantically equal
// to the Type passed as an argument.
Equal(Type) bool
Expand Down
74 changes: 37 additions & 37 deletions internal/fwschemadata/data_get_at_path_test.go

Large diffs are not rendered by default.

74 changes: 37 additions & 37 deletions internal/fwschemadata/data_get_test.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions internal/reflect/build_value_test.go
Expand Up @@ -29,7 +29,7 @@ func TestBuildValue(t *testing.T) {
"Value Conversion Error",
"An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+
"Received null value, however the target type cannot handle null values. Use the corresponding `types` package type, a pointer type or a custom type that handles null values.\n\n"+
"Path: id\nTarget Type: string\nSuggested `types` Type: types.StringType\nSuggested Pointer Type: *string",
"Path: id\nTarget Type: string\nSuggested `types` Type: types.String\nSuggested Pointer Type: *string",
),
},
},
Expand All @@ -41,7 +41,7 @@ func TestBuildValue(t *testing.T) {
"Value Conversion Error",
"An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+
"Received unknown value, however the target type cannot handle unknown values. Use the corresponding `types` package type or a custom type that handles unknown values.\n\n"+
"Path: id\nTarget Type: string\nSuggested Type: types.StringType",
"Path: id\nTarget Type: string\nSuggested Type: types.String",
),
},
},
Expand Down
4 changes: 2 additions & 2 deletions internal/reflect/into.go
Expand Up @@ -116,7 +116,7 @@ func BuildValue(ctx context.Context, typ attr.Type, val tftypes.Value, target re
"Value Conversion Error",
"An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+
"Received unknown value, however the target type cannot handle unknown values. Use the corresponding `types` package type or a custom type that handles unknown values.\n\n"+
fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested Type: %s", path.String(), target.Type(), typ.String()),
fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested Type: %s", path.String(), target.Type(), reflect.TypeOf(typ.ValueType(ctx))),
)
return target, diags
}
Expand All @@ -140,7 +140,7 @@ func BuildValue(ctx context.Context, typ attr.Type, val tftypes.Value, target re
"Value Conversion Error",
"An unexpected error was encountered trying to build a value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+
"Received null value, however the target type cannot handle null values. Use the corresponding `types` package type, a pointer type or a custom type that handles null values.\n\n"+
fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested `types` Type: %s\nSuggested Pointer Type: *%s", path.String(), target.Type(), typ.String(), target.Type()),
fmt.Sprintf("Path: %s\nTarget Type: %s\nSuggested `types` Type: %s\nSuggested Pointer Type: *%s", path.String(), target.Type(), reflect.TypeOf(typ.ValueType(ctx)), target.Type()),
)

return target, diags
Expand Down
5 changes: 5 additions & 0 deletions internal/testing/types/bool.go
Expand Up @@ -59,6 +59,11 @@ func (t BoolType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (att
return Bool{Bool: types.Bool{Value: b}, CreatedBy: t}, nil
}

// ValueType returns the Value type.
func (t BoolType) ValueType(_ context.Context) attr.Value {
return Bool{}
}

type Bool struct {
types.Bool

Expand Down
5 changes: 5 additions & 0 deletions internal/testing/types/invalid.go
Expand Up @@ -38,6 +38,11 @@ func (t InvalidType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (
return nil, fmt.Errorf("intentional ValueFromTerraform error")
}

// ValueType returns the Value type.
func (t InvalidType) ValueType(_ context.Context) attr.Value {
return Invalid{}
}

// Invalid is an attr.Value that returns errors for methods than can return errors.
type Invalid struct{}

Expand Down
5 changes: 5 additions & 0 deletions internal/testing/types/number.go
Expand Up @@ -63,6 +63,11 @@ func (t NumberType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (a
}, nil
}

// ValueType returns the Value type.
func (t NumberType) ValueType(_ context.Context) attr.Value {
return Number{}
}

type Number struct {
types.Number

Expand Down
5 changes: 5 additions & 0 deletions internal/testing/types/string.go
Expand Up @@ -62,6 +62,11 @@ func (t StringType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (a
}, nil
}

// ValueType returns the Value type.
func (t StringType) ValueType(_ context.Context) attr.Value {
return String{}
}

type String struct {
InternalString types.String

Expand Down
7 changes: 7 additions & 0 deletions types/list.go
Expand Up @@ -163,6 +163,13 @@ func (l ListType) Validate(ctx context.Context, in tftypes.Value, path path.Path
return diags
}

// ValueType returns the Value type.
func (t ListType) ValueType(_ context.Context) attr.Value {
return List{
ElemType: t.ElemType,
}
}

// List represents a list of attr.Values, all of the same type, indicated
// by ElemType.
type List struct {
Expand Down
7 changes: 7 additions & 0 deletions types/map.go
Expand Up @@ -167,6 +167,13 @@ func (m MapType) Validate(ctx context.Context, in tftypes.Value, path path.Path)
return diags
}

// ValueType returns the Value type.
func (t MapType) ValueType(_ context.Context) attr.Value {
return Map{
ElemType: t.ElemType,
}
}

// Map represents a map of attr.Values, all of the same type, indicated by
// ElemType. Keys for the map will always be strings.
type Map struct {
Expand Down
7 changes: 7 additions & 0 deletions types/object.go
Expand Up @@ -145,6 +145,13 @@ func (o ObjectType) String() string {
return res.String()
}

// ValueType returns the Value type.
func (t ObjectType) ValueType(_ context.Context) attr.Value {
return Object{
AttrTypes: t.AttrTypes,
}
}

// Object represents an object
type Object struct {
// Unknown will be set to true if the entire object is an unknown value.
Expand Down
19 changes: 19 additions & 0 deletions types/primitive.go
Expand Up @@ -92,6 +92,25 @@ func (p primitive) ValueFromTerraform(ctx context.Context, in tftypes.Value) (at
}
}

// ValueType returns the Value type.
func (p primitive) ValueType(_ context.Context) attr.Value {
// These Value do not need to be valid.
switch p {
case BoolType:
return Bool{}
case Float64Type:
return Float64{}
case Int64Type:
return Int64{}
case NumberType:
return Number{}
case StringType:
return String{}
default:
panic(fmt.Sprintf("unknown primitive %d", p))
}
}

// Equal returns true if `o` is also a primitive, and is the same type of
// primitive as `p`.
func (p primitive) Equal(o attr.Type) bool {
Expand Down
5 changes: 5 additions & 0 deletions types/primitive_test.go
Expand Up @@ -90,6 +90,11 @@ func (t testAttributeType) String() string {
panic("not implemented")
}

// ValueType returns the Value type.
func (t testAttributeType) ValueType(_ context.Context) attr.Value {
panic("not implemented")
}

func TestPrimitiveEqual(t *testing.T) {
t.Parallel()

Expand Down
7 changes: 7 additions & 0 deletions types/set.go
Expand Up @@ -195,6 +195,13 @@ func (st SetType) Validate(ctx context.Context, in tftypes.Value, path path.Path
return diags
}

// ValueType returns the Value type.
func (t SetType) ValueType(_ context.Context) attr.Value {
return Set{
ElemType: t.ElemType,
}
}

// Set represents a set of attr.Value, all of the same type,
// indicated by ElemType.
type Set struct {
Expand Down