command/format: show deep diffs for old/new JSON strings
Because so far we've not supported dynamically-typed complex data structures, several providers have used strings containing JSON to stand in for these. In order to get a readable diff in those cases, we'll recognize situations where old and new are both JSON and present a diff of the effective value of the JSON, using a faux call to the jsonencode(...) function to indicate when we've done so. This is a bit of a "cute" heuristic, but is important at least for now until we can migrate away from that practice of passing large JSON strings to providers and use dynamically-typed attributes instead.
This commit is contained in:
parent
9706a00b3a
commit
ce157c7f23
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/mitchellh/colorstring"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
|
@ -422,6 +423,24 @@ func (p *blockBodyDiffPrinter) writeValue(val cty.Value, action plans.Action, in
|
|||
case ty.IsPrimitiveType():
|
||||
switch ty {
|
||||
case cty.String:
|
||||
{
|
||||
// Special behavior for JSON strings
|
||||
src := []byte(val.AsString())
|
||||
ty, err := ctyjson.ImpliedType(src)
|
||||
if err == nil {
|
||||
jv, err := ctyjson.Unmarshal(src, ty)
|
||||
if err == nil {
|
||||
p.buf.WriteString("jsonencode(")
|
||||
p.buf.WriteByte('\n')
|
||||
p.buf.WriteString(strings.Repeat(" ", indent+4))
|
||||
p.writeValue(jv, action, indent+4)
|
||||
p.buf.WriteByte('\n')
|
||||
p.buf.WriteString(strings.Repeat(" ", indent))
|
||||
p.buf.WriteByte(')')
|
||||
break // don't *also* do the normal behavior below
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(p.buf, "%q", val.AsString())
|
||||
case cty.Bool:
|
||||
if val.True() {
|
||||
|
@ -516,12 +535,42 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa
|
|||
if old.IsKnown() && new.IsKnown() && !old.IsNull() && !new.IsNull() {
|
||||
switch {
|
||||
// TODO: list diffs using longest-common-subsequence matching algorithm
|
||||
// TODO: object diffs that behave a bit like the map diffs, including if the two object types don't exactly match
|
||||
// TODO: multi-line string diffs showing lines added/removed using longest-common-subsequence
|
||||
|
||||
case ty == cty.String:
|
||||
// We only have special behavior for multi-line strings here
|
||||
// We have special behavior for both multi-line strings in general
|
||||
// and for strings that can parse as JSON. For the JSON handling
|
||||
// to apply, both old and new must be valid JSON.
|
||||
// For single-line strings that don't parse as JSON we just fall
|
||||
// out of this switch block and do the default old -> new rendering.
|
||||
oldS := old.AsString()
|
||||
newS := new.AsString()
|
||||
|
||||
{
|
||||
// Special behavior for JSON strings
|
||||
oldBytes := []byte(oldS)
|
||||
newBytes := []byte(newS)
|
||||
oldType, oldErr := ctyjson.ImpliedType(oldBytes)
|
||||
newType, newErr := ctyjson.ImpliedType(newBytes)
|
||||
if oldErr == nil && newErr == nil {
|
||||
oldJV, oldErr := ctyjson.Unmarshal(oldBytes, oldType)
|
||||
newJV, newErr := ctyjson.Unmarshal(newBytes, newType)
|
||||
if oldErr == nil && newErr == nil {
|
||||
if !oldJV.RawEquals(newJV) { // two JSON values may differ only in insignificant whitespace
|
||||
p.buf.WriteString("jsonencode(")
|
||||
p.buf.WriteByte('\n')
|
||||
p.buf.WriteString(strings.Repeat(" ", indent+4))
|
||||
p.writeValueDiff(oldJV, newJV, indent+4, path)
|
||||
p.buf.WriteByte('\n')
|
||||
p.buf.WriteString(strings.Repeat(" ", indent))
|
||||
p.buf.WriteByte(')')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Index(oldS, "\n") < 0 && strings.Index(newS, "\n") < 0 {
|
||||
break
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue