diff --git a/.changelog/522.txt b/.changelog/522.txt new file mode 100644 index 000000000..28cbc32cd --- /dev/null +++ b/.changelog/522.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +types: Added `ListValueFrom()`, `MapValueFrom()`, `ObjectValueFrom()`, and `SetValueFrom()` functions, which can create value types from standard Go types using reflection similar to `tfsdk.ValueFrom()` +``` diff --git a/types/list.go b/types/list.go index 8b2d75319..8b3cb9e96 100644 --- a/types/list.go +++ b/types/list.go @@ -230,6 +230,35 @@ func ListValue(elementType attr.Type, elements []attr.Value) (List, diag.Diagnos }, nil } +// ListValueFrom creates a List with a known value, using reflection rules. +// The elements must be a slice which can convert into the given element type. +// Access the value via the List type Elements or ElementsAs methods. +func ListValueFrom(ctx context.Context, elementType attr.Type, elements any) (List, diag.Diagnostics) { + attrValue, diags := reflect.FromValue( + ctx, + ListType{ElemType: elementType}, + elements, + path.Empty(), + ) + + if diags.HasError() { + return ListUnknown(elementType), diags + } + + list, ok := attrValue.(List) + + // This should not happen, but ensure there is an error if it does. + if !ok { + diags.AddError( + "Unable to Convert List Value", + "An unexpected result occurred when creating a List using ListValueFrom. "+ + "This is an issue with terraform-plugin-framework and should be reported to the provider developers.", + ) + } + + return list, diags +} + // ListValueMust creates a List with a known value, converting any diagnostics // into a panic at runtime. Access the value via the List // type Elements or ElementsAs methods. diff --git a/types/list_test.go b/types/list_test.go index 78bc89839..ad87391dc 100644 --- a/types/list_test.go +++ b/types/list_test.go @@ -334,6 +334,124 @@ func TestListValue(t *testing.T) { } } +func TestListValueFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + elementType attr.Type + elements any + expected List + expectedDiags diag.Diagnostics + }{ + "valid-StringType-[]attr.Value-empty": { + elementType: StringType, + elements: []attr.Value{}, + expected: List{ + ElemType: StringType, + Elems: []attr.Value{}, + }, + }, + "valid-StringType-[]types.String-empty": { + elementType: StringType, + elements: []String{}, + expected: List{ + ElemType: StringType, + Elems: []attr.Value{}, + }, + }, + "valid-StringType-[]types.String": { + elementType: StringType, + elements: []String{ + StringNull(), + StringUnknown(), + StringValue("test"), + }, + expected: List{ + ElemType: StringType, + Elems: []attr.Value{ + String{Null: true}, + String{Unknown: true}, + String{Value: "test"}, + }, + }, + }, + "valid-StringType-[]*string": { + elementType: StringType, + elements: []*string{ + nil, + pointer("test1"), + pointer("test2"), + }, + expected: List{ + ElemType: StringType, + Elems: []attr.Value{ + String{Null: true}, + String{Value: "test1"}, + String{Value: "test2"}, + }, + }, + }, + "valid-StringType-[]string": { + elementType: StringType, + elements: []string{ + "test1", + "test2", + }, + expected: List{ + ElemType: StringType, + Elems: []attr.Value{ + String{Value: "test1"}, + String{Value: "test2"}, + }, + }, + }, + "invalid-not-slice": { + elementType: StringType, + elements: "oops", + expected: ListUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "List Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "expected List value, received tftypes.Value with value: tftypes.String<\"oops\">", + ), + }, + }, + "invalid-type": { + elementType: StringType, + elements: []bool{true}, + expected: ListUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtListIndex(0), + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "can't unmarshal tftypes.Bool into *string, expected string", + ), + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := ListValueFrom(context.Background(), testCase.elementType, testCase.elements) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + // This test verifies the assumptions that creating the Value via function then // setting the fields directly has no effects. func TestListValue_DeprecatedFieldSetting(t *testing.T) { diff --git a/types/map.go b/types/map.go index bddb4bef8..0b2319f03 100644 --- a/types/map.go +++ b/types/map.go @@ -234,6 +234,36 @@ func MapValue(elementType attr.Type, elements map[string]attr.Value) (Map, diag. }, nil } +// MapValueFrom creates a Map with a known value, using reflection rules. +// The elements must be a map of string keys to values which can convert into +// the given element type. Access the value via the Map type Elements or +// ElementsAs methods. +func MapValueFrom(ctx context.Context, elementType attr.Type, elements any) (Map, diag.Diagnostics) { + attrValue, diags := reflect.FromValue( + ctx, + MapType{ElemType: elementType}, + elements, + path.Empty(), + ) + + if diags.HasError() { + return MapUnknown(elementType), diags + } + + m, ok := attrValue.(Map) + + // This should not happen, but ensure there is an error if it does. + if !ok { + diags.AddError( + "Unable to Convert Map Value", + "An unexpected result occurred when creating a Map using MapValueFrom. "+ + "This is an issue with terraform-plugin-framework and should be reported to the provider developers.", + ) + } + + return m, diags +} + // MapValueMust creates a Map with a known value, converting any diagnostics // into a panic at runtime. Access the value via the Map // type Elements or ElementsAs methods. diff --git a/types/map_test.go b/types/map_test.go index fb9cb11b4..7920adff3 100644 --- a/types/map_test.go +++ b/types/map_test.go @@ -313,6 +313,124 @@ func TestMapValue(t *testing.T) { } } +func TestMapValueFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + elementType attr.Type + elements any + expected Map + expectedDiags diag.Diagnostics + }{ + "valid-StringType-map[string]attr.Value-empty": { + elementType: StringType, + elements: map[string]attr.Value{}, + expected: Map{ + ElemType: StringType, + Elems: map[string]attr.Value{}, + }, + }, + "valid-StringType-map[string]types.String-empty": { + elementType: StringType, + elements: map[string]String{}, + expected: Map{ + ElemType: StringType, + Elems: map[string]attr.Value{}, + }, + }, + "valid-StringType-map[string]types.String": { + elementType: StringType, + elements: map[string]String{ + "key1": StringNull(), + "key2": StringUnknown(), + "key3": StringValue("test"), + }, + expected: Map{ + ElemType: StringType, + Elems: map[string]attr.Value{ + "key1": String{Null: true}, + "key2": String{Unknown: true}, + "key3": String{Value: "test"}, + }, + }, + }, + "valid-StringType-map[string]*string": { + elementType: StringType, + elements: map[string]*string{ + "key1": nil, + "key2": pointer("test1"), + "key3": pointer("test2"), + }, + expected: Map{ + ElemType: StringType, + Elems: map[string]attr.Value{ + "key1": String{Null: true}, + "key2": String{Value: "test1"}, + "key3": String{Value: "test2"}, + }, + }, + }, + "valid-StringType-map[string]string": { + elementType: StringType, + elements: map[string]string{ + "key1": "test1", + "key2": "test2", + }, + expected: Map{ + ElemType: StringType, + Elems: map[string]attr.Value{ + "key1": String{Value: "test1"}, + "key2": String{Value: "test2"}, + }, + }, + }, + "invalid-not-map": { + elementType: StringType, + elements: "oops", + expected: MapUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Map Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "expected Map value, received tftypes.Value with value: tftypes.String<\"oops\">", + ), + }, + }, + "invalid-type": { + elementType: StringType, + elements: map[string]bool{"key1": true}, + expected: MapUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtMapKey("key1"), + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "can't unmarshal tftypes.Bool into *string, expected string", + ), + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := MapValueFrom(context.Background(), testCase.elementType, testCase.elements) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + // This test verifies the assumptions that creating the Value via function then // setting the fields directly has no effects. func TestMapValue_DeprecatedFieldSetting(t *testing.T) { diff --git a/types/object.go b/types/object.go index 4def99381..65f929714 100644 --- a/types/object.go +++ b/types/object.go @@ -239,6 +239,36 @@ func ObjectValue(attributeTypes map[string]attr.Type, attributes map[string]attr }, nil } +// ObjectValueFrom creates a Object with a known value, using reflection rules. +// The attributes must be a map of string attribute names to attribute values +// which can convert into the given attribute type or a struct with tfsdk field +// tags. Access the value via the Object type Elements or ElementsAs methods. +func ObjectValueFrom(ctx context.Context, attributeTypes map[string]attr.Type, attributes any) (Object, diag.Diagnostics) { + attrValue, diags := reflect.FromValue( + ctx, + ObjectType{AttrTypes: attributeTypes}, + attributes, + path.Empty(), + ) + + if diags.HasError() { + return ObjectUnknown(attributeTypes), diags + } + + m, ok := attrValue.(Object) + + // This should not happen, but ensure there is an error if it does. + if !ok { + diags.AddError( + "Unable to Convert Object Value", + "An unexpected result occurred when creating a Object using ObjectValueFrom. "+ + "This is an issue with terraform-plugin-framework and should be reported to the provider developers.", + ) + } + + return m, diags +} + // ObjectValueMust creates a Object with a known value, converting any diagnostics // into a panic at runtime. Access the value via the Object // type Elements or ElementsAs methods. diff --git a/types/object_test.go b/types/object_test.go index 5945c6f38..466bca9ff 100644 --- a/types/object_test.go +++ b/types/object_test.go @@ -8,6 +8,7 @@ import ( "github.com/google/go-cmp/cmp" "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" ) @@ -494,6 +495,169 @@ func TestObjectValue(t *testing.T) { } } +func TestObjectValueFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + attributeTypes map[string]attr.Type + attributes any + expected Object + expectedDiags diag.Diagnostics + }{ + "valid-*struct": { + attributeTypes: map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + attributes: pointer(struct { + Bool Bool `tfsdk:"bool"` + String String `tfsdk:"string"` + }{ + Bool: BoolValue(true), + String: StringValue("test"), + }), + expected: Object{ + AttrTypes: map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + Attrs: map[string]attr.Value{ + "bool": Bool{Value: true}, + "string": String{Value: "test"}, + }, + }, + }, + "valid-struct": { + attributeTypes: map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + attributes: struct { + Bool Bool `tfsdk:"bool"` + String String `tfsdk:"string"` + }{ + Bool: BoolValue(true), + String: StringValue("test"), + }, + expected: Object{ + AttrTypes: map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + Attrs: map[string]attr.Value{ + "bool": Bool{Value: true}, + "string": String{Value: "test"}, + }, + }, + }, + "invalid-nil": { + attributeTypes: map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }, + attributes: nil, + expected: ObjectUnknown( + map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }, + ), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "cannot construct attr.Type from (invalid)", + ), + }, + }, + // This likely should be valid, however it is not currently. + "invalid-map[string]attr.Value": { + attributeTypes: map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + attributes: map[string]attr.Value{ + "bool": BoolNull(), + "string": StringNull(), + }, + expected: ObjectUnknown( + map[string]attr.Type{ + "bool": BoolType, + "string": StringType, + }, + ), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "cannot use type map[string]attr.Value as schema type types.ObjectType; types.ObjectType must be an attr.TypeWithElementType to hold map[string]attr.Value", + ), + }, + }, + "invalid-not-struct": { + attributeTypes: map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }, + attributes: "oops", + expected: ObjectUnknown(map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "expected tftypes.Object[\"bool\":tftypes.Bool, \"string\":tftypes.String], got tftypes.String", + ), + }, + }, + "invalid-type": { + attributeTypes: map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }, + attributes: map[string]bool{"key1": true}, + expected: ObjectUnknown(map[string]attr.Type{ + "string": StringType, + "bool": BoolType, + }), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Value Conversion Error", + "An unexpected error was encountered trying to convert from value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "cannot use type map[string]bool as schema type types.ObjectType; types.ObjectType must be an attr.TypeWithElementType to hold map[string]bool", + ), + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := ObjectValueFrom(context.Background(), testCase.attributeTypes, testCase.attributes) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + for _, d := range diags { + t.Logf("%s\n%s", d.Summary(), d.Detail()) + } + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + // This test verifies the assumptions that creating the Value via function then // setting the fields directly has no effects. func TestObjectValue_DeprecatedFieldSetting(t *testing.T) { diff --git a/types/pointer_test.go b/types/pointer_test.go new file mode 100644 index 000000000..627bd9a52 --- /dev/null +++ b/types/pointer_test.go @@ -0,0 +1,5 @@ +package types + +func pointer[T any](value T) *T { + return &value +} diff --git a/types/set.go b/types/set.go index 9d3c48e11..fbfc5e66d 100644 --- a/types/set.go +++ b/types/set.go @@ -262,6 +262,35 @@ func SetValue(elementType attr.Type, elements []attr.Value) (Set, diag.Diagnosti }, nil } +// SetValueFrom creates a Set with a known value, using reflection rules. +// The elements must be a slice which can convert into the given element type. +// Access the value via the Set type Elements or ElementsAs methods. +func SetValueFrom(ctx context.Context, elementType attr.Type, elements any) (Set, diag.Diagnostics) { + attrValue, diags := reflect.FromValue( + ctx, + SetType{ElemType: elementType}, + elements, + path.Empty(), + ) + + if diags.HasError() { + return SetUnknown(elementType), diags + } + + set, ok := attrValue.(Set) + + // This should not happen, but ensure there is an error if it does. + if !ok { + diags.AddError( + "Unable to Convert Set Value", + "An unexpected result occurred when creating a Set using SetValueFrom. "+ + "This is an issue with terraform-plugin-framework and should be reported to the provider developers.", + ) + } + + return set, diags +} + // SetValueMust creates a Set with a known value, converting any diagnostics // into a panic at runtime. Access the value via the Set // type Elements or ElementsAs methods. diff --git a/types/set_test.go b/types/set_test.go index ed14fcd75..405284ccd 100644 --- a/types/set_test.go +++ b/types/set_test.go @@ -639,6 +639,124 @@ func TestSetValue(t *testing.T) { } } +func TestSetValueFrom(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + elementType attr.Type + elements any + expected Set + expectedDiags diag.Diagnostics + }{ + "valid-StringType-[]attr.Value-empty": { + elementType: StringType, + elements: []attr.Value{}, + expected: Set{ + ElemType: StringType, + Elems: []attr.Value{}, + }, + }, + "valid-StringType-[]types.String-empty": { + elementType: StringType, + elements: []String{}, + expected: Set{ + ElemType: StringType, + Elems: []attr.Value{}, + }, + }, + "valid-StringType-[]types.String": { + elementType: StringType, + elements: []String{ + StringNull(), + StringUnknown(), + StringValue("test"), + }, + expected: Set{ + ElemType: StringType, + Elems: []attr.Value{ + String{Null: true}, + String{Unknown: true}, + String{Value: "test"}, + }, + }, + }, + "valid-StringType-[]*string": { + elementType: StringType, + elements: []*string{ + nil, + pointer("test1"), + pointer("test2"), + }, + expected: Set{ + ElemType: StringType, + Elems: []attr.Value{ + String{Null: true}, + String{Value: "test1"}, + String{Value: "test2"}, + }, + }, + }, + "valid-StringType-[]string": { + elementType: StringType, + elements: []string{ + "test1", + "test2", + }, + expected: Set{ + ElemType: StringType, + Elems: []attr.Value{ + String{Value: "test1"}, + String{Value: "test2"}, + }, + }, + }, + "invalid-not-slice": { + elementType: StringType, + elements: "oops", + expected: SetUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty(), + "Set Type Validation Error", + "An unexpected error was encountered trying to validate an attribute value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "expected Set value, received tftypes.Value with value: tftypes.String<\"oops\">", + ), + }, + }, + "invalid-type": { + elementType: StringType, + elements: []bool{true}, + expected: SetUnknown(StringType), + expectedDiags: diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Empty().AtListIndex(0), + "Value Conversion Error", + "An unexpected error was encountered trying to convert the Terraform value. This is always an error in the provider. Please report the following to the provider developer:\n\n"+ + "can't unmarshal tftypes.Bool into *string, expected string", + ), + }, + }, + } + + for name, testCase := range testCases { + name, testCase := name, testCase + + t.Run(name, func(t *testing.T) { + t.Parallel() + + got, diags := SetValueFrom(context.Background(), testCase.elementType, testCase.elements) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + + if diff := cmp.Diff(diags, testCase.expectedDiags); diff != "" { + t.Errorf("unexpected diagnostics difference: %s", diff) + } + }) + } +} + // This test verifies the assumptions that creating the Value via function then // setting the fields directly has no effects. func TestSetValue_DeprecatedFieldSetting(t *testing.T) { diff --git a/website/docs/plugin/framework/types.mdx b/website/docs/plugin/framework/types.mdx index a740df8ee..03931b7ce 100644 --- a/website/docs/plugin/framework/types.mdx +++ b/website/docs/plugin/framework/types.mdx @@ -285,6 +285,7 @@ Call one of the following to create a `types.List`: * [`types.ListNull(attr.Type) types.List`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ListNull): A null list value with the given element type. * [`types.ListUnknown(attr.Type) types.List`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ListUnknown): An unknown list value with the given element type. * [`types.ListValue(attr.Type, []attr.Value) (types.List, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ListValue): A known value with the given element type and values. +* [`types.ListValueFrom(context.Context, attr.Type, any) (types.List, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ListValueFrom): A known value with the given element type and values. Can convert from standard Go types, using the [conversion rules](/plugin/framework/accessing-values#conversion-rules). * [`types.ListValueMust(map[string]attr.Type, map[string]attr.Value) types.List`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ListValueMust): A known value with the given element type and values. Any diagnostics are converted to a runtime panic. This is recommended only for testing or exhaustively tested logic. ### MapType and Map @@ -328,6 +329,7 @@ Call one of the following to create a `types.Map`: * [`types.MapNull(attr.Type)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#MapNull): A null map value with the given element type. * [`types.MapUnknown(attr.Type)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#MapUnknown): An unknown map value with the given element type. * [`types.MapValue(attr.Type, map[string]attr.Value) (types.Map, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#MapValue): A known value with the given element type and values. +* [`types.MapValueFrom(context.Context, attr.Type, any) (types.Map, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#MapValueFrom): A known value with the given element type and values. Can convert from standard Go types, using the [conversion rules](/plugin/framework/accessing-values#conversion-rules). * [`types.MapValueMust(map[string]attr.Type, map[string]attr.Value) types.Map`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#MapValueMust): A known value with the given element type and values. Any diagnostics are converted to a runtime panic. This is recommended only for testing or exhaustively tested logic. ### ObjectType and Object @@ -375,6 +377,7 @@ Call one of the following to create a `types.Object`: * [`types.ObjectNull(map[string]attr.Type) types.Object`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ObjectNull): A null object value with the given attribute type mapping. * [`types.ObjectUnknown(map[string]attr.Type) types.Object`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ObjectUnknown): An unknown object value with the given attribute type mapping. * [`types.ObjectValue(map[string]attr.Type, map[string]attr.Value) (types.Object, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ObjectValue): A known value with the given attribute type mapping and attribute value mapping. +* [`types.ObjectValueFrom(context.Context, attr.Type, any) (types.Object, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ObjectValueFrom): A known value with the given attribute type mapping and values. Can convert from standard Go types, using the [conversion rules](/plugin/framework/accessing-values#conversion-rules). * [`types.ObjectValueMust(map[string]attr.Type, map[string]attr.Value) types.Object`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#ObjectValueMust): A known value with the given attribute type mapping and attribute value mapping. Any diagnostics are converted to a runtime panic. This is recommended only for testing or exhaustively tested logic. ### SetType and Set @@ -414,6 +417,7 @@ Call one of the following to create a `types.Set`: * [`types.SetNull(attr.Type) types.Set`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#SetNull): A null list value with the given element type. * [`types.SetUnknown(attr.Type) types.Set`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#SetUnknown): An unknown list value with the given element type. * [`types.SetValue(attr.Type, []attr.Value) (types.Set, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#SetValue): A known value with the given element type and values. +* [`types.SetValueFrom(context.Context, attr.Type, any) (types.Set, diag.Diagnostics)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#SetValueFrom): A known value with the given element type and values. Can convert from standard Go types, using the [conversion rules](/plugin/framework/accessing-values#conversion-rules). * [`types.SetValueMust(map[string]attr.Type, map[string]attr.Value) types.Set`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-framework/types#SetValueMust): A known value with the given element type and values. Any diagnostics are converted to a runtime panic. This is recommended only for testing or exhaustively tested logic. ## Create Provider-Defined Types and Values