Skip to content

Commit

Permalink
Merge pull request #113 from hashicorp/bendbennett/issues-111
Browse files Browse the repository at this point in the history
Add `useJSONNumber` to `Plan` and amend `UnmarshalJSON` behavior
  • Loading branch information
bendbennett committed Dec 18, 2023
2 parents c00e873 + da9b6fa commit 41fa8df
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 1 deletion.
21 changes: 20 additions & 1 deletion plan.go
Expand Up @@ -4,6 +4,7 @@
package tfjson

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand All @@ -29,6 +30,12 @@ const (

// Plan represents the entire contents of an output Terraform plan.
type Plan struct {
// useJSONNumber opts into the behavior of calling
// json.Decoder.UseNumber prior to decoding the plan, which turns
// numbers into json.Numbers instead of float64s. Set it using
// Plan.UseJSONNumber.
useJSONNumber bool

// The version of the plan format. This should always match the
// PlanFormatVersion constant in this package, or else an unmarshal
// will be unstable.
Expand Down Expand Up @@ -85,6 +92,14 @@ type ResourceAttribute struct {
Attribute []json.RawMessage `json:"attribute"`
}

// UseJSONNumber controls whether the Plan will be decoded using the
// json.Number behavior or the float64 behavior. When b is true, the Plan will
// represent numbers in PlanOutputs as json.Numbers. When b is false, the
// Plan will represent numbers in PlanOutputs as float64s.
func (p *Plan) UseJSONNumber(b bool) {
p.useJSONNumber = b
}

// Validate checks to ensure that the plan is present, and the
// version matches the version supported by this library.
func (p *Plan) Validate() error {
Expand Down Expand Up @@ -127,7 +142,11 @@ func (p *Plan) UnmarshalJSON(b []byte) error {
type rawPlan Plan
var plan rawPlan

err := json.Unmarshal(b, &plan)
dec := json.NewDecoder(bytes.NewReader(b))
if p.useJSONNumber {
dec.UseNumber()
}
err := dec.Decode(&plan)
if err != nil {
return err
}
Expand Down
56 changes: 56 additions & 0 deletions plan_test.go
Expand Up @@ -120,3 +120,59 @@ func TestPlan_movedBlock(t *testing.T) {
t.Fatalf("unexpected previous address %s, expected is random_id.test", plan.ResourceChanges[0].PreviousAddress)
}
}

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

b, err := os.ReadFile("testdata/numerics/plan.json")
if err != nil {
t.Fatal(err)
}

testCases := map[string]struct {
useJSONNumber bool
expected any
}{
"float64": {
expected: 1.23,
},
"json-number": {
useJSONNumber: true,
expected: json.Number("1.23"),
},
}

for name, testCase := range testCases {
name, testCase := name, testCase

t.Run(name, func(t *testing.T) {
t.Parallel()

plan := &Plan{}

plan.UseJSONNumber(testCase.useJSONNumber)

err = plan.UnmarshalJSON(b)

if err != nil {
t.Fatal(err)
}

after, ok := plan.ResourceChanges[0].Change.After.(map[string]any)

if !ok {
t.Fatal("plan.ResourceChanges[0].Change.After cannot be asserted as map[string]any")
}

attr, ok := after["configurable_attribute"]

if !ok {
t.Fatal("configurable attribute not found")
}

if diff := cmp.Diff(attr, testCase.expected); diff != "" {
t.Errorf("unexpected difference: %s", diff)
}
})
}
}
1 change: 1 addition & 0 deletions testdata/numerics/plan.json
@@ -0,0 +1 @@
{"format_version":"1.2","terraform_version":"1.6.5","planned_values":{"root_module":{"resources":[{"address":"example_resource.test","mode":"managed","type":"example_resource","name":"test","provider_name":"registry.terraform.io/hashicorp/example","schema_version":0,"values":{"configurable_attribute":1.23,"id":"one"},"sensitive_values":{}}]}},"resource_changes":[{"address":"example_resource.test","mode":"managed","type":"example_resource","name":"test","provider_name":"registry.terraform.io/hashicorp/example","change":{"actions":["create"],"before":null,"after":{"configurable_attribute":1.23,"id":"one"},"after_unknown":{},"before_sensitive":false,"after_sensitive":{}}}],"configuration":{"provider_config":{"example":{"name":"example","full_name":"registry.terraform.io/hashicorp/example"}},"root_module":{"resources":[{"address":"example_resource.test","mode":"managed","type":"example_resource","name":"test","provider_config_key":"example","expressions":{"configurable_attribute":{"constant_value":1.23},"id":{"constant_value":"one"}},"schema_version":0}]}},"timestamp":"2023-12-07T13:55:56Z"}

0 comments on commit 41fa8df

Please sign in to comment.