diff --git a/.changelog/214.txt b/.changelog/214.txt new file mode 100644 index 00000000..f1553460 --- /dev/null +++ b/.changelog/214.txt @@ -0,0 +1,3 @@ +```release-note:bug +tftypes: Clarified `ValueFromJSON` error messaging with object attribute key issues +``` diff --git a/tftypes/value_json.go b/tftypes/value_json.go index d21c880f..b889b3be 100644 --- a/tftypes/value_json.go +++ b/tftypes/value_json.go @@ -453,15 +453,15 @@ func jsonUnmarshalObject(buf []byte, attrTypes map[string]Type, p *AttributePath vals := map[string]Value{} for dec.More() { - innerPath := p.WithElementKeyValue(NewValue(String, UnknownValue)) tok, err := dec.Token() if err != nil { - return Value{}, innerPath.NewErrorf("error reading token: %w", err) + return Value{}, p.NewErrorf("error reading object attribute key token: %w", err) } key, ok := tok.(string) if !ok { - return Value{}, innerPath.NewErrorf("object attribute key was %T, not string", tok) + return Value{}, p.NewErrorf("object attribute key was %T with value %v, not string", tok, tok) } + innerPath := p.WithAttributeName(key) attrType, ok := attrTypes[key] if !ok { if opts.IgnoreUndefinedAttributes { @@ -472,7 +472,6 @@ func jsonUnmarshalObject(buf []byte, attrTypes map[string]Type, p *AttributePath return Value{}, innerPath.NewErrorf("unsupported attribute %q", key) } - innerPath = p.WithAttributeName(key) var rawVal json.RawMessage err = dec.Decode(&rawVal) diff --git a/tftypes/value_json_test.go b/tftypes/value_json_test.go index f3253fc6..b24462a5 100644 --- a/tftypes/value_json_test.go +++ b/tftypes/value_json_test.go @@ -1,6 +1,7 @@ package tftypes import ( + "fmt" "math/big" "testing" @@ -10,9 +11,10 @@ import ( func TestValueFromJSON(t *testing.T) { t.Parallel() type testCase struct { - value Value - typ Type - json string + value Value + typ Type + json string + expectedError error } tests := map[string]testCase{ // Primitives @@ -211,6 +213,30 @@ func TestValueFromJSON(t *testing.T) { }, json: `{}`, }, + "object-attribute-key-token-error": { + value: Value{}, + typ: Object{ + AttributeTypes: map[string]Type{}, + }, + json: `{{}}`, + expectedError: AttributePathError{ + Path: NewAttributePath(), + err: fmt.Errorf("error reading object attribute key token: invalid character '{'"), + }, + }, + "object-attribute-key-missing-error": { + value: Value{}, + typ: Object{ + AttributeTypes: map[string]Type{ + "test": String, + }, + }, + json: `{"not-test": "test-value"}`, + expectedError: AttributePathError{ + Path: NewAttributePath().WithAttributeName("not-test"), + err: fmt.Errorf("unsupported attribute \"not-test\""), + }, + }, "object-of-bool_number": { value: NewValue(Object{ AttributeTypes: map[string]Type{ @@ -371,8 +397,8 @@ func TestValueFromJSON(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() val, err := ValueFromJSON([]byte(test.json), test.typ) - if err != nil { - t.Fatalf("unexpected error unmarshaling: %s", err) + if diff := cmp.Diff(test.expectedError, err); diff != "" { + t.Errorf("unexpected error difference: %s", diff) } if diff := cmp.Diff(test.value, val); diff != "" { t.Errorf("Unexpected results (-wanted +got): %s", diff)