From 357012a2f3ee1c005eb604c43251de5c28fb58e6 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Fri, 21 Apr 2023 09:51:55 +0200 Subject: [PATCH] Refactor of differ to make code reuse easier (#33054) * refactor of differ to make code reuse easier * fix imports --- .../computed/renderers/primitive.go | 2 +- internal/command/jsonformat/diff.go | 12 +- .../command/jsonformat/differ/attribute.go | 41 +-- internal/command/jsonformat/differ/block.go | 29 +- internal/command/jsonformat/differ/differ.go | 12 + .../differ/{change_test.go => differ_test.go} | 298 +++++++++--------- internal/command/jsonformat/differ/list.go | 33 +- internal/command/jsonformat/differ/map.go | 25 +- internal/command/jsonformat/differ/object.go | 21 +- internal/command/jsonformat/differ/output.go | 7 +- .../command/jsonformat/differ/primitive.go | 6 +- .../command/jsonformat/differ/sensitive.go | 41 +-- internal/command/jsonformat/differ/set.go | 43 +-- internal/command/jsonformat/differ/tuple.go | 11 +- internal/command/jsonformat/differ/unknown.go | 38 +-- internal/command/jsonformat/jsondiff/diff.go | 3 +- internal/command/jsonformat/plan_test.go | 8 +- internal/command/jsonformat/renderer.go | 5 +- internal/command/jsonformat/state.go | 12 +- .../attribute_path/matcher.go | 0 .../attribute_path/matcher_test.go | 0 .../{differ => structured}/change.go | 21 +- internal/command/jsonformat/structured/doc.go | 6 + .../change_map.go => structured/map.go} | 36 ++- .../jsonformat/structured/sensitive.go | 15 + .../change_slice.go => structured/slice.go} | 13 +- .../command/jsonformat/structured/unknown.go | 8 + 27 files changed, 409 insertions(+), 337 deletions(-) create mode 100644 internal/command/jsonformat/differ/differ.go rename internal/command/jsonformat/differ/{change_test.go => differ_test.go} (93%) rename internal/command/jsonformat/{differ => structured}/attribute_path/matcher.go (100%) rename internal/command/jsonformat/{differ => structured}/attribute_path/matcher_test.go (100%) rename internal/command/jsonformat/{differ => structured}/change.go (92%) create mode 100644 internal/command/jsonformat/structured/doc.go rename internal/command/jsonformat/{differ/change_map.go => structured/map.go} (67%) create mode 100644 internal/command/jsonformat/structured/sensitive.go rename internal/command/jsonformat/{differ/change_slice.go => structured/slice.go} (82%) create mode 100644 internal/command/jsonformat/structured/unknown.go diff --git a/internal/command/jsonformat/computed/renderers/primitive.go b/internal/command/jsonformat/computed/renderers/primitive.go index 9891945c8b..075d03432a 100644 --- a/internal/command/jsonformat/computed/renderers/primitive.go +++ b/internal/command/jsonformat/computed/renderers/primitive.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/plans" ) diff --git a/internal/command/jsonformat/diff.go b/internal/command/jsonformat/diff.go index 9ddd50abf3..fc8b600090 100644 --- a/internal/command/jsonformat/diff.go +++ b/internal/command/jsonformat/diff.go @@ -3,7 +3,8 @@ package jsonformat import ( "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/differ" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonplan" "github.com/hashicorp/terraform/internal/plans" ) @@ -42,22 +43,25 @@ func precomputeDiffs(plan Plan, mode plans.Mode) diffs { } schema := plan.getSchema(drift) + change := structured.FromJsonChange(drift.Change, relevantAttrs) diffs.drift = append(diffs.drift, diff{ change: drift, - diff: differ.FromJsonChange(drift.Change, relevantAttrs).ComputeDiffForBlock(schema.Block), + diff: differ.ComputeDiffForBlock(change, schema.Block), }) } for _, change := range plan.ResourceChanges { schema := plan.getSchema(change) + structuredChange := structured.FromJsonChange(change.Change, attribute_path.AlwaysMatcher()) diffs.changes = append(diffs.changes, diff{ change: change, - diff: differ.FromJsonChange(change.Change, attribute_path.AlwaysMatcher()).ComputeDiffForBlock(schema.Block), + diff: differ.ComputeDiffForBlock(structuredChange, schema.Block), }) } for key, output := range plan.OutputChanges { - diffs.outputs[key] = differ.FromJsonChange(output, attribute_path.AlwaysMatcher()).ComputeDiffForOutput() + change := structured.FromJsonChange(output, attribute_path.AlwaysMatcher()) + diffs.outputs[key] = differ.ComputeDiffForOutput(change) } return diffs diff --git a/internal/command/jsonformat/differ/attribute.go b/internal/command/jsonformat/differ/attribute.go index 4219095068..e1c913ddc7 100644 --- a/internal/command/jsonformat/differ/attribute.go +++ b/internal/command/jsonformat/differ/attribute.go @@ -1,6 +1,7 @@ package differ import ( + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/zclconf/go-cty/cty" ctyjson "github.com/zclconf/go-cty/cty/json" @@ -9,42 +10,42 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonprovider" ) -func (change Change) ComputeDiffForAttribute(attribute *jsonprovider.Attribute) computed.Diff { +func ComputeDiffForAttribute(change structured.Change, attribute *jsonprovider.Attribute) computed.Diff { if attribute.AttributeNestedType != nil { - return change.computeDiffForNestedAttribute(attribute.AttributeNestedType) + return computeDiffForNestedAttribute(change, attribute.AttributeNestedType) } - return change.ComputeDiffForType(unmarshalAttribute(attribute)) + return ComputeDiffForType(change, unmarshalAttribute(attribute)) } -func (change Change) computeDiffForNestedAttribute(nested *jsonprovider.NestedType) computed.Diff { - if sensitive, ok := change.checkForSensitiveNestedAttribute(nested); ok { +func computeDiffForNestedAttribute(change structured.Change, nested *jsonprovider.NestedType) computed.Diff { + if sensitive, ok := checkForSensitiveNestedAttribute(change, nested); ok { return sensitive } - if computed, ok := change.checkForUnknownNestedAttribute(nested); ok { + if computed, ok := checkForUnknownNestedAttribute(change, nested); ok { return computed } switch NestingMode(nested.NestingMode) { case nestingModeSingle, nestingModeGroup: - return change.computeAttributeDiffAsNestedObject(nested.Attributes) + return computeAttributeDiffAsNestedObject(change, nested.Attributes) case nestingModeMap: - return change.computeAttributeDiffAsNestedMap(nested.Attributes) + return computeAttributeDiffAsNestedMap(change, nested.Attributes) case nestingModeList: - return change.computeAttributeDiffAsNestedList(nested.Attributes) + return computeAttributeDiffAsNestedList(change, nested.Attributes) case nestingModeSet: - return change.computeAttributeDiffAsNestedSet(nested.Attributes) + return computeAttributeDiffAsNestedSet(change, nested.Attributes) default: panic("unrecognized nesting mode: " + nested.NestingMode) } } -func (change Change) ComputeDiffForType(ctype cty.Type) computed.Diff { - if sensitive, ok := change.checkForSensitiveType(ctype); ok { +func ComputeDiffForType(change structured.Change, ctype cty.Type) computed.Diff { + if sensitive, ok := checkForSensitiveType(change, ctype); ok { return sensitive } - if computed, ok := change.checkForUnknownType(ctype); ok { + if computed, ok := checkForUnknownType(change, ctype); ok { return computed } @@ -56,19 +57,19 @@ func (change Change) ComputeDiffForType(ctype cty.Type) computed.Diff { // function computeChangeForDynamicValues(), but external callers will // only be in this situation when processing outputs so this function // is named for their benefit. - return change.ComputeDiffForOutput() + return ComputeDiffForOutput(change) case ctype.IsPrimitiveType(): - return change.computeAttributeDiffAsPrimitive(ctype) + return computeAttributeDiffAsPrimitive(change, ctype) case ctype.IsObjectType(): - return change.computeAttributeDiffAsObject(ctype.AttributeTypes()) + return computeAttributeDiffAsObject(change, ctype.AttributeTypes()) case ctype.IsMapType(): - return change.computeAttributeDiffAsMap(ctype.ElementType()) + return computeAttributeDiffAsMap(change, ctype.ElementType()) case ctype.IsListType(): - return change.computeAttributeDiffAsList(ctype.ElementType()) + return computeAttributeDiffAsList(change, ctype.ElementType()) case ctype.IsTupleType(): - return change.computeAttributeDiffAsTuple(ctype.TupleElementTypes()) + return computeAttributeDiffAsTuple(change, ctype.TupleElementTypes()) case ctype.IsSetType(): - return change.computeAttributeDiffAsSet(ctype.ElementType()) + return computeAttributeDiffAsSet(change, ctype.ElementType()) default: panic("unrecognized type: " + ctype.FriendlyName()) } diff --git a/internal/command/jsonformat/differ/block.go b/internal/command/jsonformat/differ/block.go index 3e4af7ec1a..9b2375c76f 100644 --- a/internal/command/jsonformat/differ/block.go +++ b/internal/command/jsonformat/differ/block.go @@ -4,26 +4,27 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) -func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Diff { - if sensitive, ok := change.checkForSensitiveBlock(block); ok { +func ComputeDiffForBlock(change structured.Change, block *jsonprovider.Block) computed.Diff { + if sensitive, ok := checkForSensitiveBlock(change, block); ok { return sensitive } - if unknown, ok := change.checkForUnknownBlock(block); ok { + if unknown, ok := checkForUnknownBlock(change, block); ok { return unknown } - current := change.getDefaultActionForIteration() + current := change.GetDefaultActionForIteration() - blockValue := change.asMap() + blockValue := change.AsMap() attributes := make(map[string]computed.Diff) for key, attr := range block.Attributes { - childValue := blockValue.getChild(key) + childValue := blockValue.GetChild(key) if !childValue.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. @@ -43,7 +44,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif childValue.BeforeExplicit = false childValue.AfterExplicit = false - childChange := childValue.ComputeDiffForAttribute(attr) + childChange := ComputeDiffForAttribute(childValue, attr) if childChange.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values at all in blocks. continue @@ -64,20 +65,20 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif } for key, blockType := range block.BlockTypes { - childValue := blockValue.getChild(key) + childValue := blockValue.GetChild(key) if !childValue.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. childValue = childValue.AsNoOp() } - beforeSensitive := childValue.isBeforeSensitive() - afterSensitive := childValue.isAfterSensitive() + beforeSensitive := childValue.IsBeforeSensitive() + afterSensitive := childValue.IsAfterSensitive() forcesReplacement := childValue.ReplacePaths.Matches() switch NestingMode(blockType.NestingMode) { case nestingModeSet: - diffs, action := childValue.computeBlockDiffsAsSet(blockType.Block) + diffs, action := computeBlockDiffsAsSet(childValue, blockType.Block) if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values in blocks. continue @@ -85,7 +86,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif blocks.AddAllSetBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) current = collections.CompareActions(current, action) case nestingModeList: - diffs, action := childValue.computeBlockDiffsAsList(blockType.Block) + diffs, action := computeBlockDiffsAsList(childValue, blockType.Block) if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values in blocks. continue @@ -93,7 +94,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif blocks.AddAllListBlock(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) current = collections.CompareActions(current, action) case nestingModeMap: - diffs, action := childValue.computeBlockDiffsAsMap(blockType.Block) + diffs, action := computeBlockDiffsAsMap(childValue, blockType.Block) if action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values in blocks. continue @@ -101,7 +102,7 @@ func (change Change) ComputeDiffForBlock(block *jsonprovider.Block) computed.Dif blocks.AddAllMapBlocks(key, diffs, forcesReplacement, beforeSensitive, afterSensitive) current = collections.CompareActions(current, action) case nestingModeSingle, nestingModeGroup: - diff := childValue.ComputeDiffForBlock(blockType.Block) + diff := ComputeDiffForBlock(childValue, blockType.Block) if diff.Action == plans.NoOp && childValue.Before == nil && childValue.After == nil { // Don't record nil values in blocks. continue diff --git a/internal/command/jsonformat/differ/differ.go b/internal/command/jsonformat/differ/differ.go new file mode 100644 index 0000000000..3ca584e8d4 --- /dev/null +++ b/internal/command/jsonformat/differ/differ.go @@ -0,0 +1,12 @@ +package differ + +import ( + "github.com/hashicorp/terraform/internal/command/jsonformat/computed" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" +) + +// asDiff is a helper function to abstract away some simple and common +// functionality when converting a renderer into a concrete diff. +func asDiff(change structured.Change, renderer computed.DiffRenderer) computed.Diff { + return computed.NewDiff(renderer, change.CalculateAction(), change.ReplacePaths.Matches()) +} diff --git a/internal/command/jsonformat/differ/change_test.go b/internal/command/jsonformat/differ/differ_test.go similarity index 93% rename from internal/command/jsonformat/differ/change_test.go rename to internal/command/jsonformat/differ/differ_test.go index bccbf7b5db..5945f2f1e5 100644 --- a/internal/command/jsonformat/differ/change_test.go +++ b/internal/command/jsonformat/differ/differ_test.go @@ -9,7 +9,8 @@ import ( ctyjson "github.com/zclconf/go-cty/cty/json" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) @@ -41,12 +42,12 @@ func TestValue_SimpleBlocks(t *testing.T) { // individual test cases within blocks for some simple tests. tcs := map[string]struct { - input Change + input structured.Change block *jsonprovider.Block validate renderers.ValidateDiffFunction }{ "delete_with_null_sensitive_value": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "normal_attribute": "some value", }, @@ -71,7 +72,7 @@ func TestValue_SimpleBlocks(t *testing.T) { }, nil, nil, nil, nil, plans.Delete, false), }, "create_with_null_sensitive_value": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "normal_attribute": "some value", @@ -107,7 +108,7 @@ func TestValue_SimpleBlocks(t *testing.T) { } t.Run(name, func(t *testing.T) { - tc.validate(t, tc.input.ComputeDiffForBlock(tc.block)) + tc.validate(t, ComputeDiffForBlock(tc.input, tc.block)) }) } } @@ -119,7 +120,7 @@ func TestValue_ObjectAttributes(t *testing.T) { // types of collections. tcs := map[string]struct { - input Change + input structured.Change attributes map[string]cty.Type validateSingleDiff renderers.ValidateDiffFunction validateObject renderers.ValidateDiffFunction @@ -133,7 +134,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateSetDiffs *SetDiff }{ "create": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "attribute_one": "new", @@ -149,7 +150,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateReplace: false, }, "delete": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -165,7 +166,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateReplace: false, }, "create_sensitive": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "attribute_one": "new", @@ -191,7 +192,7 @@ func TestValue_ObjectAttributes(t *testing.T) { false), }, "delete_sensitive": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -209,7 +210,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, plans.Delete, false), true, false, plans.Delete, false), }, "create_unknown": { - input: Change{ + input: structured.Change{ Before: nil, After: nil, Unknown: true, @@ -220,7 +221,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateSingleDiff: renderers.ValidateUnknown(nil, plans.Create, false), }, "update_unknown": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -250,7 +251,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "create_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: map[string]interface{}{ "attribute_one": "new", @@ -280,7 +281,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "create_attribute_from_explicit_null": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": nil, }, @@ -312,7 +313,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "delete_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -342,7 +343,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "delete_attribute_to_explicit_null": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -374,7 +375,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "update_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -408,7 +409,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "create_sensitive_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: map[string]interface{}{ "attribute_one": "new", @@ -441,7 +442,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "delete_sensitive_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -474,7 +475,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "update_sensitive_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -514,7 +515,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "create_computed_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: map[string]interface{}{}, Unknown: map[string]interface{}{ @@ -531,7 +532,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateReplace: false, }, "update_computed_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -569,7 +570,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "ignores_unset_fields": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: map[string]interface{}{}, }, @@ -581,7 +582,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validateReplace: false, }, "update_replace_self": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -620,7 +621,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "update_replace_attribute": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old", }, @@ -659,7 +660,7 @@ func TestValue_ObjectAttributes(t *testing.T) { }, }, "update_includes_relevant_attributes": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "attribute_one": "old_one", "attribute_two": "old_two", @@ -740,17 +741,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateObject != nil { - tc.validateObject(t, tc.input.ComputeDiffForAttribute(attribute)) + tc.validateObject(t, ComputeDiffForAttribute(tc.input, attribute)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, tc.input.ComputeDiffForAttribute(attribute)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) return } validate := renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, tc.input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(tc.input, attribute)) }) t.Run("map", func(t *testing.T) { @@ -764,7 +765,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -772,14 +773,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -790,7 +791,7 @@ func TestValue_ObjectAttributes(t *testing.T) { input := wrapChangeInSlice(tc.input) if tc.validateList != nil { - tc.validateList(t, input.ComputeDiffForAttribute(attribute)) + tc.validateList(t, ComputeDiffForAttribute(input, attribute)) return } @@ -798,7 +799,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -806,14 +807,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -830,7 +831,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateObject)) return ret }(), collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -838,7 +839,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -846,14 +847,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) @@ -875,17 +876,17 @@ func TestValue_ObjectAttributes(t *testing.T) { } if tc.validateNestedObject != nil { - tc.validateNestedObject(t, tc.input.ComputeDiffForAttribute(attribute)) + tc.validateNestedObject(t, ComputeDiffForAttribute(tc.input, attribute)) return } if tc.validateSingleDiff != nil { - tc.validateSingleDiff(t, tc.input.ComputeDiffForAttribute(attribute)) + tc.validateSingleDiff(t, ComputeDiffForAttribute(tc.input, attribute)) return } validate := renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace) - validate(t, tc.input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(tc.input, attribute)) }) t.Run("map", func(t *testing.T) { @@ -910,7 +911,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -918,14 +919,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -950,7 +951,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -958,14 +959,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateNestedList([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -993,7 +994,7 @@ func TestValue_ObjectAttributes(t *testing.T) { ret = append(ret, tc.validateSetDiffs.After.Validate(renderers.ValidateNestedObject)) return ret }(), collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -1001,7 +1002,7 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateNestedObject, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } @@ -1009,14 +1010,14 @@ func TestValue_ObjectAttributes(t *testing.T) { validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateSingleDiff, }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ renderers.ValidateNestedObject(tc.validateDiffs, tc.validateAction, tc.validateReplace), }, collectionDefaultAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) } @@ -1223,7 +1224,7 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { t.Run(name, func(t *testing.T) { t.Run("single", func(t *testing.T) { - input := Change{ + input := structured.Change{ Before: map[string]interface{}{ "block_type": tc.before, }, @@ -1246,10 +1247,10 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { validate := renderers.ValidateBlock(nil, map[string]renderers.ValidateDiffFunction{ "block_type": tc.validate, }, nil, nil, nil, plans.Update, false) - validate(t, input.ComputeDiffForBlock(block)) + validate(t, ComputeDiffForBlock(input, block)) }) t.Run("map", func(t *testing.T) { - input := Change{ + input := structured.Change{ Before: map[string]interface{}{ "block_type": map[string]interface{}{ "one": tc.before, @@ -1278,10 +1279,10 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { "one": tc.validate, }, }, nil, plans.Update, false) - validate(t, input.ComputeDiffForBlock(block)) + validate(t, ComputeDiffForBlock(input, block)) }) t.Run("list", func(t *testing.T) { - input := Change{ + input := structured.Change{ Before: map[string]interface{}{ "block_type": []interface{}{ tc.before, @@ -1310,10 +1311,10 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { tc.validate, }, }, nil, nil, plans.Update, false) - validate(t, input.ComputeDiffForBlock(block)) + validate(t, ComputeDiffForBlock(input, block)) }) t.Run("set", func(t *testing.T) { - input := Change{ + input := structured.Change{ Before: map[string]interface{}{ "block_type": []interface{}{ tc.before, @@ -1345,7 +1346,7 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { return []renderers.ValidateDiffFunction{tc.validate} }(), }, plans.Update, false) - validate(t, input.ComputeDiffForBlock(block)) + validate(t, ComputeDiffForBlock(input, block)) }) }) } @@ -1353,18 +1354,18 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) { func TestValue_Outputs(t *testing.T) { tcs := map[string]struct { - input Change + input structured.Change validateDiff renderers.ValidateDiffFunction }{ "primitive_create": { - input: Change{ + input: structured.Change{ Before: nil, After: "new", }, validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false), }, "object_create": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "element_one": "new_one", @@ -1377,7 +1378,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Create, false), }, "list_create": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{ "new_one", @@ -1390,14 +1391,14 @@ func TestValue_Outputs(t *testing.T) { }, plans.Create, false), }, "primitive_update": { - input: Change{ + input: structured.Change{ Before: "old", After: "new", }, validateDiff: renderers.ValidatePrimitive("old", "new", plans.Update, false), }, "object_update": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element_one": "old_one", "element_two": "old_two", @@ -1413,7 +1414,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Update, false), }, "list_update": { - input: Change{ + input: structured.Change{ Before: []interface{}{ "old_one", "old_two", @@ -1431,14 +1432,14 @@ func TestValue_Outputs(t *testing.T) { }, plans.Update, false), }, "primitive_delete": { - input: Change{ + input: structured.Change{ Before: "old", After: nil, }, validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false), }, "object_delete": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element_one": "old_one", "element_two": "old_two", @@ -1451,7 +1452,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Delete, false), }, "list_delete": { - input: Change{ + input: structured.Change{ Before: []interface{}{ "old_one", "old_two", @@ -1464,7 +1465,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Delete, false), }, "primitive_to_list": { - input: Change{ + input: structured.Change{ Before: "old", After: []interface{}{ "new_one", @@ -1479,7 +1480,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Create, false), plans.Update, false), }, "primitive_to_object": { - input: Change{ + input: structured.Change{ Before: "old", After: map[string]interface{}{ "element_one": "new_one", @@ -1494,7 +1495,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Create, false), plans.Update, false), }, "list_to_primitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{ "old_one", "old_two", @@ -1510,7 +1511,7 @@ func TestValue_Outputs(t *testing.T) { plans.Update, false), }, "list_to_object": { - input: Change{ + input: structured.Change{ Before: []interface{}{ "old_one", "old_two", @@ -1531,7 +1532,7 @@ func TestValue_Outputs(t *testing.T) { }, plans.Create, false), plans.Update, false), }, "object_to_primitive": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element_one": "old_one", "element_two": "old_two", @@ -1547,7 +1548,7 @@ func TestValue_Outputs(t *testing.T) { plans.Update, false), }, "object_to_list": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element_one": "old_one", "element_two": "old_two", @@ -1580,7 +1581,7 @@ func TestValue_Outputs(t *testing.T) { } t.Run(name, func(t *testing.T) { - tc.validateDiff(t, tc.input.ComputeDiffForOutput()) + tc.validateDiff(t, ComputeDiffForOutput(tc.input)) }) } } @@ -1591,27 +1592,27 @@ func TestValue_PrimitiveAttributes(t *testing.T) { // contexts of collections. tcs := map[string]struct { - input Change + input structured.Change attribute cty.Type validateDiff renderers.ValidateDiffFunction validateSliceDiffs []renderers.ValidateDiffFunction // Lists are special in some cases. }{ "primitive_create": { - input: Change{ + input: structured.Change{ After: "new", }, attribute: cty.String, validateDiff: renderers.ValidatePrimitive(nil, "new", plans.Create, false), }, "primitive_delete": { - input: Change{ + input: structured.Change{ Before: "old", }, attribute: cty.String, validateDiff: renderers.ValidatePrimitive("old", nil, plans.Delete, false), }, "primitive_update": { - input: Change{ + input: structured.Change{ Before: "old", After: "new", }, @@ -1623,7 +1624,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "primitive_set_explicit_null": { - input: Change{ + input: structured.Change{ Before: "old", After: nil, AfterExplicit: true, @@ -1636,7 +1637,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "primitive_unset_explicit_null": { - input: Change{ + input: structured.Change{ BeforeExplicit: true, Before: nil, After: "new", @@ -1649,7 +1650,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "primitive_create_sensitive": { - input: Change{ + input: structured.Change{ Before: nil, After: "new", AfterSensitive: true, @@ -1658,7 +1659,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive(nil, "new", plans.Create, false), false, true, plans.Create, false), }, "primitive_delete_sensitive": { - input: Change{ + input: structured.Change{ Before: "old", BeforeSensitive: true, After: nil, @@ -1667,7 +1668,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidatePrimitive("old", nil, plans.Delete, false), true, false, plans.Delete, false), }, "primitive_update_sensitive": { - input: Change{ + input: structured.Change{ Before: "old", BeforeSensitive: true, After: "new", @@ -1681,7 +1682,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "primitive_create_computed": { - input: Change{ + input: structured.Change{ Before: nil, After: nil, Unknown: true, @@ -1690,7 +1691,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), }, "primitive_update_computed": { - input: Change{ + input: structured.Change{ Before: "old", After: nil, Unknown: true, @@ -1703,7 +1704,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "primitive_update_replace": { - input: Change{ + input: structured.Change{ Before: "old", After: "new", ReplacePaths: &attribute_path.PathMatcher{ @@ -1720,7 +1721,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "noop": { - input: Change{ + input: structured.Change{ Before: "old", After: "old", }, @@ -1728,7 +1729,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validateDiff: renderers.ValidatePrimitive("old", "old", plans.NoOp, false), }, "dynamic": { - input: Change{ + input: structured.Change{ Before: "old", After: "new", }, @@ -1740,7 +1741,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { }, }, "dynamic_type_change": { - input: Change{ + input: structured.Change{ Before: "old", After: 4.0, }, @@ -1773,7 +1774,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { t.Run(name, func(t *testing.T) { t.Run("direct", func(t *testing.T) { - tc.validateDiff(t, tc.input.ComputeDiffForAttribute(&jsonprovider.Attribute{ + tc.validateDiff(t, ComputeDiffForAttribute(tc.input, &jsonprovider.Attribute{ AttributeType: unmarshalType(t, tc.attribute), })) }) @@ -1787,7 +1788,7 @@ func TestValue_PrimitiveAttributes(t *testing.T) { validate := renderers.ValidateMap(map[string]renderers.ValidateDiffFunction{ "element": tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("list", func(t *testing.T) { @@ -1798,14 +1799,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateList(tc.validateSliceDiffs, defaultCollectionsAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateList([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) t.Run("set", func(t *testing.T) { @@ -1816,14 +1817,14 @@ func TestValue_PrimitiveAttributes(t *testing.T) { if tc.validateSliceDiffs != nil { validate := renderers.ValidateSet(tc.validateSliceDiffs, defaultCollectionsAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) return } validate := renderers.ValidateSet([]renderers.ValidateDiffFunction{ tc.validateDiff, }, defaultCollectionsAction, false) - validate(t, input.ComputeDiffForAttribute(attribute)) + validate(t, ComputeDiffForAttribute(input, attribute)) }) }) } @@ -1834,12 +1835,12 @@ func TestValue_CollectionAttributes(t *testing.T) { // generally cover editing collections except in special cases as editing // collections is handled automatically by other functions. tcs := map[string]struct { - input Change + input structured.Change attribute *jsonprovider.Attribute validateDiff renderers.ValidateDiffFunction }{ "map_create_empty": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{}, }, @@ -1849,7 +1850,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateMap(nil, plans.Create, false), }, "map_create_populated": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "element_one": "one", @@ -1865,7 +1866,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Create, false), }, "map_delete_empty": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: nil, }, @@ -1875,7 +1876,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateMap(nil, plans.Delete, false), }, "map_delete_populated": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element_one": "one", "element_two": "two", @@ -1891,7 +1892,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Delete, false), }, "map_create_sensitive": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{}, AfterSensitive: true, @@ -1902,7 +1903,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Create, false), false, true, plans.Create, false), }, "map_update_sensitive": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "element": "one", }, @@ -1918,7 +1919,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Update, false), true, true, plans.Update, false), }, "map_delete_sensitive": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, BeforeSensitive: true, After: nil, @@ -1929,7 +1930,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateMap(nil, plans.Delete, false), true, false, plans.Delete, false), }, "map_create_unknown": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{}, Unknown: true, @@ -1940,7 +1941,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), }, "map_update_unknown": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{}, After: map[string]interface{}{ "element": "one", @@ -1953,7 +1954,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(renderers.ValidateMap(nil, plans.Delete, false), plans.Update, false), }, "list_create_empty": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, }, @@ -1963,7 +1964,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateList(nil, plans.Create, false), }, "list_create_populated": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{"one", "two"}, }, @@ -1976,7 +1977,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Create, false), }, "list_delete_empty": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, After: nil, }, @@ -1986,7 +1987,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateList(nil, plans.Delete, false), }, "list_delete_populated": { - input: Change{ + input: structured.Change{ Before: []interface{}{"one", "two"}, After: nil, }, @@ -1999,7 +2000,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Delete, false), }, "list_create_sensitive": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, AfterSensitive: true, @@ -2010,7 +2011,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Create, false), false, true, plans.Create, false), }, "list_update_sensitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{"one"}, BeforeSensitive: true, After: []interface{}{}, @@ -2024,7 +2025,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Update, false), true, true, plans.Update, false), }, "list_delete_sensitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, BeforeSensitive: true, After: nil, @@ -2035,7 +2036,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateList(nil, plans.Delete, false), true, false, plans.Delete, false), }, "list_create_unknown": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, Unknown: true, @@ -2046,7 +2047,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), }, "list_update_unknown": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, After: []interface{}{"one"}, Unknown: true, @@ -2057,7 +2058,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(renderers.ValidateList(nil, plans.Delete, false), plans.Update, false), }, "set_create_empty": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, }, @@ -2067,7 +2068,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSet(nil, plans.Create, false), }, "set_create_populated": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{"one", "two"}, }, @@ -2080,7 +2081,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Create, false), }, "set_delete_empty": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, After: nil, }, @@ -2090,7 +2091,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSet(nil, plans.Delete, false), }, "set_delete_populated": { - input: Change{ + input: structured.Change{ Before: []interface{}{"one", "two"}, After: nil, }, @@ -2103,7 +2104,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Delete, false), }, "set_create_sensitive": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, AfterSensitive: true, @@ -2114,7 +2115,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Create, false), false, true, plans.Create, false), }, "set_update_sensitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{"one"}, BeforeSensitive: true, After: []interface{}{}, @@ -2128,7 +2129,7 @@ func TestValue_CollectionAttributes(t *testing.T) { }, plans.Update, false), true, true, plans.Update, false), }, "set_delete_sensitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, BeforeSensitive: true, After: nil, @@ -2139,7 +2140,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateSensitive(renderers.ValidateSet(nil, plans.Delete, false), true, false, plans.Delete, false), }, "set_create_unknown": { - input: Change{ + input: structured.Change{ Before: nil, After: []interface{}{}, Unknown: true, @@ -2150,7 +2151,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(nil, plans.Create, false), }, "set_update_unknown": { - input: Change{ + input: structured.Change{ Before: []interface{}{}, After: []interface{}{"one"}, Unknown: true, @@ -2161,7 +2162,7 @@ func TestValue_CollectionAttributes(t *testing.T) { validateDiff: renderers.ValidateUnknown(renderers.ValidateSet(nil, plans.Delete, false), plans.Update, false), }, "tuple_primitive": { - input: Change{ + input: structured.Change{ Before: []interface{}{ "one", 2.0, @@ -2195,19 +2196,19 @@ func TestValue_CollectionAttributes(t *testing.T) { } t.Run(name, func(t *testing.T) { - tc.validateDiff(t, tc.input.ComputeDiffForAttribute(tc.attribute)) + tc.validateDiff(t, ComputeDiffForAttribute(tc.input, tc.attribute)) }) } } func TestRelevantAttributes(t *testing.T) { tcs := map[string]struct { - input Change + input structured.Change block *jsonprovider.Block validate renderers.ValidateDiffFunction }{ "simple_attributes": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "id": "old_id", "ignore": "doesn't matter", @@ -2240,7 +2241,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, nil, nil, plans.Update, false), }, "nested_attributes": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "list_block": []interface{}{ map[string]interface{}{ @@ -2297,7 +2298,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, plans.Update, false), }, "nested_attributes_in_object": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "object": map[string]interface{}{ "id": "old_id", @@ -2333,7 +2334,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, nil, nil, plans.Update, false), }, "elements_in_list": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "list": []interface{}{ 0, 1, 2, 3, 4, @@ -2384,7 +2385,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, nil, nil, plans.Update, false), }, "elements_in_map": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "map": map[string]interface{}{ "key_one": "value_one", @@ -2433,7 +2434,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, nil, nil, plans.Update, false), }, "elements_in_set": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "set": []interface{}{ 0, 1, 2, 3, 4, @@ -2473,7 +2474,7 @@ func TestRelevantAttributes(t *testing.T) { }, nil, nil, nil, nil, plans.Update, false), }, "dynamic_types": { - input: Change{ + input: structured.Change{ Before: map[string]interface{}{ "dynamic_nested_type": map[string]interface{}{ "nested_id": "nomatch", @@ -2547,7 +2548,7 @@ func TestRelevantAttributes(t *testing.T) { tc.input.ReplacePaths = &attribute_path.PathMatcher{} } t.Run(name, func(t *testing.T) { - tc.validate(t, tc.input.ComputeDiffForBlock(tc.block)) + tc.validate(t, ComputeDiffForBlock(tc.input, tc.block)) }) } } @@ -2557,12 +2558,12 @@ func TestSpecificCases(t *testing.T) { // cases and will execute against them. For testing/fixing specific issues // you can generally put the test case in here. tcs := map[string]struct { - input Change + input structured.Change block *jsonprovider.Block validate renderers.ValidateDiffFunction }{ "issues/33016/unknown": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "triggers": map[string]interface{}{}, @@ -2598,7 +2599,7 @@ func TestSpecificCases(t *testing.T) { }, nil, nil, nil, nil, plans.Create, false), }, "issues/33016/null": { - input: Change{ + input: structured.Change{ Before: nil, After: map[string]interface{}{ "triggers": map[string]interface{}{ @@ -2636,7 +2637,7 @@ func TestSpecificCases(t *testing.T) { } for name, tc := range tcs { t.Run(name, func(t *testing.T) { - tc.validate(t, tc.input.ComputeDiffForBlock(tc.block)) + tc.validate(t, ComputeDiffForBlock(tc.input, tc.block)) }) } } @@ -2654,7 +2655,7 @@ func unmarshalType(t *testing.T, ctyType cty.Type) json.RawMessage { // wrapChangeInSlice does the same as wrapChangeInMap, except it wraps it into a // slice internally. -func wrapChangeInSlice(input Change) Change { +func wrapChangeInSlice(input structured.Change) structured.Change { return wrapChange(input, float64(0), func(value interface{}, unknown interface{}, explicit bool) interface{} { switch value.(type) { case nil: @@ -2669,9 +2670,10 @@ func wrapChangeInSlice(input Change) Change { }) } -// wrapChangeInMap access a single Change and returns a new Change that represents -// a map with a single element. That single element is the input value. -func wrapChangeInMap(input Change) Change { +// wrapChangeInMap access a single structured.Change and returns a new +// structured.Change that represents a map with a single element. That single +// element is the input value. +func wrapChangeInMap(input structured.Change) structured.Change { return wrapChange(input, "element", func(value interface{}, unknown interface{}, explicit bool) interface{} { switch value.(type) { case nil: @@ -2689,7 +2691,7 @@ func wrapChangeInMap(input Change) Change { }) } -func wrapChange(input Change, step interface{}, wrap func(interface{}, interface{}, bool) interface{}) Change { +func wrapChange(input structured.Change, step interface{}, wrap func(interface{}, interface{}, bool) interface{}) structured.Change { replacePaths := &attribute_path.PathMatcher{} for _, path := range input.ReplacePaths.(*attribute_path.PathMatcher).Paths { @@ -2715,7 +2717,7 @@ func wrapChange(input Change, step interface{}, wrap func(interface{}, interface relevantAttributes = newRelevantAttributes } - return Change{ + return structured.Change{ Before: wrap(input.Before, nil, input.BeforeExplicit), After: wrap(input.After, input.Unknown, input.AfterExplicit), Unknown: wrap(input.Unknown, nil, false), diff --git a/internal/command/jsonformat/differ/list.go b/internal/command/jsonformat/differ/list.go index 3d5440402e..0ce937df46 100644 --- a/internal/command/jsonformat/differ/list.go +++ b/internal/command/jsonformat/differ/list.go @@ -6,16 +6,17 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) -func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.Diff { - sliceValue := change.asSlice() +func computeAttributeDiffAsList(change structured.Change, elementType cty.Type) computed.Diff { + sliceValue := change.AsSlice() processIndices := func(beforeIx, afterIx int) computed.Diff { - value := sliceValue.getChild(beforeIx, afterIx) + value := sliceValue.GetChild(beforeIx, afterIx) // It's actually really difficult to render the diffs when some indices // within a slice are relevant and others aren't. To make this simpler @@ -37,7 +38,7 @@ func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.D // after. value.RelevantAttributes = attribute_path.AlwaysMatcher() - return value.ComputeDiffForType(elementType) + return ComputeDiffForType(value, elementType) } isObjType := func(_ interface{}) bool { @@ -48,11 +49,11 @@ func (change Change) computeAttributeDiffAsList(elementType cty.Type) computed.D return computed.NewDiff(renderers.List(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeAttributeDiffAsNestedList(attributes map[string]*jsonprovider.Attribute) computed.Diff { +func computeAttributeDiffAsNestedList(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { var elements []computed.Diff - current := change.getDefaultActionForIteration() - change.processNestedList(func(value Change) { - element := value.computeDiffForNestedAttribute(&jsonprovider.NestedType{ + current := change.GetDefaultActionForIteration() + processNestedList(change, func(value structured.Change) { + element := computeDiffForNestedAttribute(value, &jsonprovider.NestedType{ Attributes: attributes, NestingMode: "single", }) @@ -62,21 +63,21 @@ func (change Change) computeAttributeDiffAsNestedList(attributes map[string]*jso return computed.NewDiff(renderers.NestedList(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeBlockDiffsAsList(block *jsonprovider.Block) ([]computed.Diff, plans.Action) { +func computeBlockDiffsAsList(change structured.Change, block *jsonprovider.Block) ([]computed.Diff, plans.Action) { var elements []computed.Diff - current := change.getDefaultActionForIteration() - change.processNestedList(func(value Change) { - element := value.ComputeDiffForBlock(block) + current := change.GetDefaultActionForIteration() + processNestedList(change, func(value structured.Change) { + element := ComputeDiffForBlock(value, block) elements = append(elements, element) current = collections.CompareActions(current, element.Action) }) return elements, current } -func (change Change) processNestedList(process func(value Change)) { - sliceValue := change.asSlice() +func processNestedList(change structured.Change, process func(value structured.Change)) { + sliceValue := change.AsSlice() for ix := 0; ix < len(sliceValue.Before) || ix < len(sliceValue.After); ix++ { - value := sliceValue.getChild(ix, ix) + value := sliceValue.GetChild(ix, ix) if !value.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. value = value.AsNoOp() diff --git a/internal/command/jsonformat/differ/map.go b/internal/command/jsonformat/differ/map.go index 6f92622e63..7c90fdc1fd 100644 --- a/internal/command/jsonformat/differ/map.go +++ b/internal/command/jsonformat/differ/map.go @@ -6,12 +6,13 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) -func (change Change) computeAttributeDiffAsMap(elementType cty.Type) computed.Diff { - mapValue := change.asMap() +func computeAttributeDiffAsMap(change structured.Change, elementType cty.Type) computed.Diff { + mapValue := change.AsMap() // The jsonplan package will have stripped out unknowns from our after value // so we're going to add them back in here. @@ -45,25 +46,25 @@ func (change Change) computeAttributeDiffAsMap(elementType cty.Type) computed.Di } elements, current := collections.TransformMap(mapValue.Before, after, func(key string) computed.Diff { - value := mapValue.getChild(key) + value := mapValue.GetChild(key) if !value.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. value = value.AsNoOp() } - return value.ComputeDiffForType(elementType) + return ComputeDiffForType(value, elementType) }) return computed.NewDiff(renderers.Map(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeAttributeDiffAsNestedMap(attributes map[string]*jsonprovider.Attribute) computed.Diff { - mapValue := change.asMap() +func computeAttributeDiffAsNestedMap(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { + mapValue := change.AsMap() elements, current := collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff { - value := mapValue.getChild(key) + value := mapValue.GetChild(key) if !value.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. value = value.AsNoOp() } - return value.computeDiffForNestedAttribute(&jsonprovider.NestedType{ + return computeDiffForNestedAttribute(value, &jsonprovider.NestedType{ Attributes: attributes, NestingMode: "single", }) @@ -71,14 +72,14 @@ func (change Change) computeAttributeDiffAsNestedMap(attributes map[string]*json return computed.NewDiff(renderers.NestedMap(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeBlockDiffsAsMap(block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) { - mapValue := change.asMap() +func computeBlockDiffsAsMap(change structured.Change, block *jsonprovider.Block) (map[string]computed.Diff, plans.Action) { + mapValue := change.AsMap() return collections.TransformMap(mapValue.Before, mapValue.After, func(key string) computed.Diff { - value := mapValue.getChild(key) + value := mapValue.GetChild(key) if !value.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. value = value.AsNoOp() } - return value.ComputeDiffForBlock(block) + return ComputeDiffForBlock(value, block) }) } diff --git a/internal/command/jsonformat/differ/object.go b/internal/command/jsonformat/differ/object.go index 98b5c7e92e..657089af97 100644 --- a/internal/command/jsonformat/differ/object.go +++ b/internal/command/jsonformat/differ/object.go @@ -6,20 +6,21 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) -func (change Change) computeAttributeDiffAsObject(attributes map[string]cty.Type) computed.Diff { - attributeDiffs, action := processObject(change, attributes, func(value Change, ctype cty.Type) computed.Diff { - return value.ComputeDiffForType(ctype) +func computeAttributeDiffAsObject(change structured.Change, attributes map[string]cty.Type) computed.Diff { + attributeDiffs, action := processObject(change, attributes, func(value structured.Change, ctype cty.Type) computed.Diff { + return ComputeDiffForType(value, ctype) }) return computed.NewDiff(renderers.Object(attributeDiffs), action, change.ReplacePaths.Matches()) } -func (change Change) computeAttributeDiffAsNestedObject(attributes map[string]*jsonprovider.Attribute) computed.Diff { - attributeDiffs, action := processObject(change, attributes, func(value Change, attribute *jsonprovider.Attribute) computed.Diff { - return value.ComputeDiffForAttribute(attribute) +func computeAttributeDiffAsNestedObject(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { + attributeDiffs, action := processObject(change, attributes, func(value structured.Change, attribute *jsonprovider.Attribute) computed.Diff { + return ComputeDiffForAttribute(value, attribute) }) return computed.NewDiff(renderers.NestedObject(attributeDiffs), action, change.ReplacePaths.Matches()) } @@ -35,13 +36,13 @@ func (change Change) computeAttributeDiffAsNestedObject(attributes map[string]*j // Also, as it generic we cannot make this function a method on Change as you // can't create generic methods on structs. Instead, we make this a generic // function that receives the value as an argument. -func processObject[T any](v Change, attributes map[string]T, computeDiff func(Change, T) computed.Diff) (map[string]computed.Diff, plans.Action) { +func processObject[T any](v structured.Change, attributes map[string]T, computeDiff func(structured.Change, T) computed.Diff) (map[string]computed.Diff, plans.Action) { attributeDiffs := make(map[string]computed.Diff) - mapValue := v.asMap() + mapValue := v.AsMap() - currentAction := v.getDefaultActionForIteration() + currentAction := v.GetDefaultActionForIteration() for key, attribute := range attributes { - attributeValue := mapValue.getChild(key) + attributeValue := mapValue.GetChild(key) if !attributeValue.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. diff --git a/internal/command/jsonformat/differ/output.go b/internal/command/jsonformat/differ/output.go index 248ecc1a73..e7b211c45a 100644 --- a/internal/command/jsonformat/differ/output.go +++ b/internal/command/jsonformat/differ/output.go @@ -5,14 +5,15 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" ) -func (change Change) ComputeDiffForOutput() computed.Diff { - if sensitive, ok := change.checkForSensitiveType(cty.DynamicPseudoType); ok { +func ComputeDiffForOutput(change structured.Change) computed.Diff { + if sensitive, ok := checkForSensitiveType(change, cty.DynamicPseudoType); ok { return sensitive } - if unknown, ok := change.checkForUnknownType(cty.DynamicPseudoType); ok { + if unknown, ok := checkForUnknownType(change, cty.DynamicPseudoType); ok { return unknown } diff --git a/internal/command/jsonformat/differ/primitive.go b/internal/command/jsonformat/differ/primitive.go index 7db9b878fb..a1dcc6ec21 100644 --- a/internal/command/jsonformat/differ/primitive.go +++ b/internal/command/jsonformat/differ/primitive.go @@ -4,10 +4,10 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" - "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" ) -func (change Change) computeAttributeDiffAsPrimitive(ctype cty.Type) computed.Diff { - return change.asDiff(renderers.Primitive(change.Before, change.After, ctype)) +func computeAttributeDiffAsPrimitive(change structured.Change, ctype cty.Type) computed.Diff { + return asDiff(change, renderers.Primitive(change.Before, change.After, ctype)) } diff --git a/internal/command/jsonformat/differ/sensitive.go b/internal/command/jsonformat/differ/sensitive.go index 97324d86f6..2a2e914433 100644 --- a/internal/command/jsonformat/differ/sensitive.go +++ b/internal/command/jsonformat/differ/sensitive.go @@ -5,33 +5,34 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) type CreateSensitiveRenderer func(computed.Diff, bool, bool) computed.DiffRenderer -func (change Change) checkForSensitiveType(ctype cty.Type) (computed.Diff, bool) { - return change.checkForSensitive(renderers.Sensitive, func(value Change) computed.Diff { - return value.ComputeDiffForType(ctype) +func checkForSensitiveType(change structured.Change, ctype cty.Type) (computed.Diff, bool) { + return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff { + return ComputeDiffForType(value, ctype) }) } -func (change Change) checkForSensitiveNestedAttribute(attribute *jsonprovider.NestedType) (computed.Diff, bool) { - return change.checkForSensitive(renderers.Sensitive, func(value Change) computed.Diff { - return value.computeDiffForNestedAttribute(attribute) +func checkForSensitiveNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) { + return checkForSensitive(change, renderers.Sensitive, func(value structured.Change) computed.Diff { + return computeDiffForNestedAttribute(value, attribute) }) } -func (change Change) checkForSensitiveBlock(block *jsonprovider.Block) (computed.Diff, bool) { - return change.checkForSensitive(renderers.SensitiveBlock, func(value Change) computed.Diff { - return value.ComputeDiffForBlock(block) +func checkForSensitiveBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) { + return checkForSensitive(change, renderers.SensitiveBlock, func(value structured.Change) computed.Diff { + return ComputeDiffForBlock(value, block) }) } -func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedDiff func(value Change) computed.Diff) (computed.Diff, bool) { - beforeSensitive := change.isBeforeSensitive() - afterSensitive := change.isAfterSensitive() +func checkForSensitive(change structured.Change, create CreateSensitiveRenderer, computedDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) { + beforeSensitive := change.IsBeforeSensitive() + afterSensitive := change.IsAfterSensitive() if !beforeSensitive && !afterSensitive { return computed.Diff{}, false @@ -44,7 +45,7 @@ func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedD // The change can choose what to do with this information, in most cases // it will just be ignored in favour of printing `(sensitive value)`. - value := Change{ + value := structured.Change{ BeforeExplicit: change.BeforeExplicit, AfterExplicit: change.AfterExplicit, Before: change.Before, @@ -75,17 +76,3 @@ func (change Change) checkForSensitive(create CreateSensitiveRenderer, computedD return computed.NewDiff(create(inner, beforeSensitive, afterSensitive), action, change.ReplacePaths.Matches()), true } - -func (change Change) isBeforeSensitive() bool { - if sensitive, ok := change.BeforeSensitive.(bool); ok { - return sensitive - } - return false -} - -func (change Change) isAfterSensitive() bool { - if sensitive, ok := change.AfterSensitive.(bool); ok { - return sensitive - } - return false -} diff --git a/internal/command/jsonformat/differ/set.go b/internal/command/jsonformat/differ/set.go index 4c244c5500..8e0bccbbd4 100644 --- a/internal/command/jsonformat/differ/set.go +++ b/internal/command/jsonformat/differ/set.go @@ -8,27 +8,28 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/plans" ) -func (change Change) computeAttributeDiffAsSet(elementType cty.Type) computed.Diff { +func computeAttributeDiffAsSet(change structured.Change, elementType cty.Type) computed.Diff { var elements []computed.Diff - current := change.getDefaultActionForIteration() - change.processSet(func(value Change) { - element := value.ComputeDiffForType(elementType) + current := change.GetDefaultActionForIteration() + processSet(change, func(value structured.Change) { + element := ComputeDiffForType(value, elementType) elements = append(elements, element) current = collections.CompareActions(current, element.Action) }) return computed.NewDiff(renderers.Set(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeAttributeDiffAsNestedSet(attributes map[string]*jsonprovider.Attribute) computed.Diff { +func computeAttributeDiffAsNestedSet(change structured.Change, attributes map[string]*jsonprovider.Attribute) computed.Diff { var elements []computed.Diff - current := change.getDefaultActionForIteration() - change.processSet(func(value Change) { - element := value.computeDiffForNestedAttribute(&jsonprovider.NestedType{ + current := change.GetDefaultActionForIteration() + processSet(change, func(value structured.Change) { + element := computeDiffForNestedAttribute(value, &jsonprovider.NestedType{ Attributes: attributes, NestingMode: "single", }) @@ -38,19 +39,19 @@ func (change Change) computeAttributeDiffAsNestedSet(attributes map[string]*json return computed.NewDiff(renderers.NestedSet(elements), current, change.ReplacePaths.Matches()) } -func (change Change) computeBlockDiffsAsSet(block *jsonprovider.Block) ([]computed.Diff, plans.Action) { +func computeBlockDiffsAsSet(change structured.Change, block *jsonprovider.Block) ([]computed.Diff, plans.Action) { var elements []computed.Diff - current := change.getDefaultActionForIteration() - change.processSet(func(value Change) { - element := value.ComputeDiffForBlock(block) + current := change.GetDefaultActionForIteration() + processSet(change, func(value structured.Change) { + element := ComputeDiffForBlock(value, block) elements = append(elements, element) current = collections.CompareActions(current, element.Action) }) return elements, current } -func (change Change) processSet(process func(value Change)) { - sliceValue := change.asSlice() +func processSet(change structured.Change, process func(value structured.Change)) { + sliceValue := change.AsSlice() foundInBefore := make(map[int]int) foundInAfter := make(map[int]int) @@ -67,8 +68,8 @@ func (change Change) processSet(process func(value Change)) { continue } - child := sliceValue.getChild(ix, jx) - if reflect.DeepEqual(child.Before, child.After) && child.isBeforeSensitive() == child.isAfterSensitive() && !child.isUnknown() { + child := sliceValue.GetChild(ix, jx) + if reflect.DeepEqual(child.Before, child.After) && child.IsBeforeSensitive() == child.IsAfterSensitive() && !child.IsUnknown() { matched = true foundInBefore[ix] = jx foundInAfter[jx] = ix @@ -80,7 +81,7 @@ func (change Change) processSet(process func(value Change)) { } } - clearRelevantStatus := func(change Change) Change { + clearRelevantStatus := func(change structured.Change) structured.Change { // It's actually really difficult to render the diffs when some indices // within a slice are relevant and others aren't. To make this simpler // we just treat all children of a relevant list or set as also @@ -112,11 +113,11 @@ func (change Change) processSet(process func(value Change)) { for ix := 0; ix < len(sliceValue.Before); ix++ { if jx := foundInBefore[ix]; jx >= 0 { - child := clearRelevantStatus(sliceValue.getChild(ix, jx)) + child := clearRelevantStatus(sliceValue.GetChild(ix, jx)) process(child) continue } - child := clearRelevantStatus(sliceValue.getChild(ix, len(sliceValue.After))) + child := clearRelevantStatus(sliceValue.GetChild(ix, len(sliceValue.After))) process(child) } @@ -125,7 +126,7 @@ func (change Change) processSet(process func(value Change)) { // Then this value was handled in the previous for loop. continue } - child := clearRelevantStatus(sliceValue.getChild(len(sliceValue.Before), jx)) + child := clearRelevantStatus(sliceValue.GetChild(len(sliceValue.Before), jx)) process(child) } } diff --git a/internal/command/jsonformat/differ/tuple.go b/internal/command/jsonformat/differ/tuple.go index 3885be71e8..f45023b0f6 100644 --- a/internal/command/jsonformat/differ/tuple.go +++ b/internal/command/jsonformat/differ/tuple.go @@ -6,19 +6,20 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" ) -func (change Change) computeAttributeDiffAsTuple(elementTypes []cty.Type) computed.Diff { +func computeAttributeDiffAsTuple(change structured.Change, elementTypes []cty.Type) computed.Diff { var elements []computed.Diff - current := change.getDefaultActionForIteration() - sliceValue := change.asSlice() + current := change.GetDefaultActionForIteration() + sliceValue := change.AsSlice() for ix, elementType := range elementTypes { - childValue := sliceValue.getChild(ix, ix) + childValue := sliceValue.GetChild(ix, ix) if !childValue.RelevantAttributes.MatchesPartial() { // Mark non-relevant attributes as unchanged. childValue = childValue.AsNoOp() } - element := childValue.ComputeDiffForType(elementType) + element := ComputeDiffForType(childValue, elementType) elements = append(elements, element) current = collections.CompareActions(current, element.Action) } diff --git a/internal/command/jsonformat/differ/unknown.go b/internal/command/jsonformat/differ/unknown.go index e574a36e14..7486a928af 100644 --- a/internal/command/jsonformat/differ/unknown.go +++ b/internal/command/jsonformat/differ/unknown.go @@ -4,18 +4,17 @@ import ( "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" - "github.com/hashicorp/terraform/internal/command/jsonformat/computed/renderers" - + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" ) -func (change Change) checkForUnknownType(ctype cty.Type) (computed.Diff, bool) { - return change.checkForUnknown(false, func(value Change) computed.Diff { - return value.ComputeDiffForType(ctype) +func checkForUnknownType(change structured.Change, ctype cty.Type) (computed.Diff, bool) { + return checkForUnknown(change, false, func(value structured.Change) computed.Diff { + return ComputeDiffForType(value, ctype) }) } -func (change Change) checkForUnknownNestedAttribute(attribute *jsonprovider.NestedType) (computed.Diff, bool) { +func checkForUnknownNestedAttribute(change structured.Change, attribute *jsonprovider.NestedType) (computed.Diff, bool) { // We want our child attributes to show up as computed instead of deleted. // Let's populate that here. @@ -24,12 +23,12 @@ func (change Change) checkForUnknownNestedAttribute(attribute *jsonprovider.Nest childUnknown[key] = true } - return change.checkForUnknown(childUnknown, func(value Change) computed.Diff { - return value.computeDiffForNestedAttribute(attribute) + return checkForUnknown(change, childUnknown, func(value structured.Change) computed.Diff { + return computeDiffForNestedAttribute(value, attribute) }) } -func (change Change) checkForUnknownBlock(block *jsonprovider.Block) (computed.Diff, bool) { +func checkForUnknownBlock(change structured.Change, block *jsonprovider.Block) (computed.Diff, bool) { // We want our child attributes to show up as computed instead of deleted. // Let's populate that here. @@ -38,13 +37,13 @@ func (change Change) checkForUnknownBlock(block *jsonprovider.Block) (computed.D childUnknown[key] = true } - return change.checkForUnknown(childUnknown, func(value Change) computed.Diff { - return value.ComputeDiffForBlock(block) + return checkForUnknown(change, childUnknown, func(value structured.Change) computed.Diff { + return ComputeDiffForBlock(value, block) }) } -func (change Change) checkForUnknown(childUnknown interface{}, computeDiff func(value Change) computed.Diff) (computed.Diff, bool) { - unknown := change.isUnknown() +func checkForUnknown(change structured.Change, childUnknown interface{}, computeDiff func(value structured.Change) computed.Diff) (computed.Diff, bool) { + unknown := change.IsUnknown() if !unknown { return computed.Diff{}, false @@ -56,26 +55,19 @@ func (change Change) checkForUnknown(childUnknown interface{}, computeDiff func( change.AfterExplicit = true if change.Before == nil { - return change.asDiff(renderers.Unknown(computed.Diff{})), true + return asDiff(change, renderers.Unknown(computed.Diff{})), true } // If we get here, then we have a before value. We're going to model a // delete operation and our renderer later can render the overall change // accurately. - beforeValue := Change{ + beforeValue := structured.Change{ Before: change.Before, BeforeSensitive: change.BeforeSensitive, Unknown: childUnknown, ReplacePaths: change.ReplacePaths, RelevantAttributes: change.RelevantAttributes, } - return change.asDiff(renderers.Unknown(computeDiff(beforeValue))), true -} - -func (change Change) isUnknown() bool { - if unknown, ok := change.Unknown.(bool); ok { - return unknown - } - return false + return asDiff(change, renderers.Unknown(computeDiff(beforeValue))), true } diff --git a/internal/command/jsonformat/jsondiff/diff.go b/internal/command/jsonformat/jsondiff/diff.go index f8f0172e22..1c620b482f 100644 --- a/internal/command/jsonformat/jsondiff/diff.go +++ b/internal/command/jsonformat/jsondiff/diff.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/collections" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/plans" ) @@ -150,7 +150,6 @@ func (opts JsonOpts) processObject(before, after map[string]interface{}, relevan // Mark non-relevant attributes as unchanged. afterChild = beforeChild afterExplicit = beforeExplicit - } return opts.Transform(beforeChild, afterChild, beforeExplicit, afterExplicit, childRelevantAttributes) diff --git a/internal/command/jsonformat/plan_test.go b/internal/command/jsonformat/plan_test.go index c906e1103b..077d29afa0 100644 --- a/internal/command/jsonformat/plan_test.go +++ b/internal/command/jsonformat/plan_test.go @@ -11,7 +11,8 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/command/jsonformat/differ" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonplan" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/configs/configschema" @@ -6654,12 +6655,11 @@ func runTestCases(t *testing.T, testCases map[string]testCase) { } jsonschemas := jsonprovider.MarshalForRenderer(tfschemas) + change := structured.FromJsonChange(jsonchanges[0].Change, attribute_path.AlwaysMatcher()) renderer := Renderer{Colorize: color} diff := diff{ change: jsonchanges[0], - diff: differ. - FromJsonChange(jsonchanges[0].Change, attribute_path.AlwaysMatcher()). - ComputeDiffForBlock(jsonschemas[jsonchanges[0].ProviderName].ResourceSchemas[jsonchanges[0].Type].Block), + diff: differ.ComputeDiffForBlock(change, jsonschemas[jsonchanges[0].ProviderName].ResourceSchemas[jsonchanges[0].Type].Block), } output, _ := renderHumanDiff(renderer, diff, proposedChange) if diff := cmp.Diff(output, tc.ExpectedOutput); diff != "" { diff --git a/internal/command/jsonformat/renderer.go b/internal/command/jsonformat/renderer.go index c1adf5c0c1..676d6cad7d 100644 --- a/internal/command/jsonformat/renderer.go +++ b/internal/command/jsonformat/renderer.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/internal/command/format" "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/differ" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonplan" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/command/jsonstate" @@ -112,7 +113,7 @@ func (r Renderer) RenderLog(log *JSONLog) error { if len(log.Outputs) > 0 { r.Streams.Println(r.Colorize.Color("[bold][green]Outputs:[reset]")) for name, output := range log.Outputs { - change := differ.FromJsonViewsOutput(output) + change := structured.FromJsonViewsOutput(output) ctype, err := ctyjson.UnmarshalType(output.Type) if err != nil { return err @@ -121,7 +122,7 @@ func (r Renderer) RenderLog(log *JSONLog) error { opts := computed.NewRenderHumanOpts(r.Colorize) opts.ShowUnchangedChildren = true - outputDiff := change.ComputeDiffForType(ctype) + outputDiff := differ.ComputeDiffForType(change, ctype) outputStr := outputDiff.RenderHuman(0, opts) msg := fmt.Sprintf("%s = %s", name, outputStr) diff --git a/internal/command/jsonformat/state.go b/internal/command/jsonformat/state.go index 0c181bca51..a86838ff16 100644 --- a/internal/command/jsonformat/state.go +++ b/internal/command/jsonformat/state.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/terraform/internal/command/jsonformat/computed" "github.com/hashicorp/terraform/internal/command/jsonformat/differ" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured" "github.com/hashicorp/terraform/internal/command/jsonprovider" "github.com/hashicorp/terraform/internal/command/jsonstate" ) @@ -63,9 +64,11 @@ func (state State) renderHumanStateModule(renderer Renderer, module jsonstate.Mo schema := state.GetSchema(resource) switch resource.Mode { case jsonstate.ManagedResourceMode: - renderer.Streams.Printf("resource %q %q %s", resource.Type, resource.Name, differ.FromJsonResource(resource).ComputeDiffForBlock(schema.Block).RenderHuman(0, opts)) + change := structured.FromJsonResource(resource) + renderer.Streams.Printf("resource %q %q %s", resource.Type, resource.Name, differ.ComputeDiffForBlock(change, schema.Block).RenderHuman(0, opts)) case jsonstate.DataResourceMode: - renderer.Streams.Printf("data %q %q %s", resource.Type, resource.Name, differ.FromJsonResource(resource).ComputeDiffForBlock(schema.Block).RenderHuman(0, opts)) + change := structured.FromJsonResource(resource) + renderer.Streams.Printf("data %q %q %s", resource.Type, resource.Name, differ.ComputeDiffForBlock(change, schema.Block).RenderHuman(0, opts)) default: panic("found unrecognized resource mode: " + resource.Mode) } @@ -91,13 +94,14 @@ func (state State) renderHumanStateOutputs(renderer Renderer, opts computed.Rend for _, key := range keys { output := state.RootModuleOutputs[key] + change := structured.FromJsonOutput(output) ctype, err := ctyjson.UnmarshalType(output.Type) if err != nil { // We can actually do this without the type, so even if we fail // to work out the type let's just render this anyway. - renderer.Streams.Printf("%s = %s\n", key, differ.FromJsonOutput(state.RootModuleOutputs[key]).ComputeDiffForOutput().RenderHuman(0, opts)) + renderer.Streams.Printf("%s = %s\n", key, differ.ComputeDiffForOutput(change).RenderHuman(0, opts)) } else { - renderer.Streams.Printf("%s = %s\n", key, differ.FromJsonOutput(state.RootModuleOutputs[key]).ComputeDiffForType(ctype).RenderHuman(0, opts)) + renderer.Streams.Printf("%s = %s\n", key, differ.ComputeDiffForType(change, ctype).RenderHuman(0, opts)) } } } diff --git a/internal/command/jsonformat/differ/attribute_path/matcher.go b/internal/command/jsonformat/structured/attribute_path/matcher.go similarity index 100% rename from internal/command/jsonformat/differ/attribute_path/matcher.go rename to internal/command/jsonformat/structured/attribute_path/matcher.go diff --git a/internal/command/jsonformat/differ/attribute_path/matcher_test.go b/internal/command/jsonformat/structured/attribute_path/matcher_test.go similarity index 100% rename from internal/command/jsonformat/differ/attribute_path/matcher_test.go rename to internal/command/jsonformat/structured/attribute_path/matcher_test.go diff --git a/internal/command/jsonformat/differ/change.go b/internal/command/jsonformat/structured/change.go similarity index 92% rename from internal/command/jsonformat/differ/change.go rename to internal/command/jsonformat/structured/change.go index e561b47a2b..be8d68a461 100644 --- a/internal/command/jsonformat/differ/change.go +++ b/internal/command/jsonformat/structured/change.go @@ -1,11 +1,10 @@ -package differ +package structured import ( "encoding/json" "reflect" - "github.com/hashicorp/terraform/internal/command/jsonformat/computed" - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" "github.com/hashicorp/terraform/internal/command/jsonplan" "github.com/hashicorp/terraform/internal/command/jsonstate" viewsjson "github.com/hashicorp/terraform/internal/command/views/json" @@ -163,11 +162,13 @@ func FromJsonViewsOutput(output viewsjson.Output) Change { } } -func (change Change) asDiff(renderer computed.DiffRenderer) computed.Diff { - return computed.NewDiff(renderer, change.calculateChange(), change.ReplacePaths.Matches()) -} +// AsDiff -func (change Change) calculateChange() plans.Action { +// CalculateAction does a very simple analysis to make the best guess at the +// action this change describes. For complex types such as objects, maps, lists, +// or sets it is likely more efficient to work out the action directly instead +// of relying on this function. +func (change Change) CalculateAction() plans.Action { if (change.Before == nil && !change.BeforeExplicit) && (change.After != nil || change.AfterExplicit) { return plans.Create } @@ -175,14 +176,14 @@ func (change Change) calculateChange() plans.Action { return plans.Delete } - if reflect.DeepEqual(change.Before, change.After) && change.AfterExplicit == change.BeforeExplicit && change.isAfterSensitive() == change.isBeforeSensitive() { + if reflect.DeepEqual(change.Before, change.After) && change.AfterExplicit == change.BeforeExplicit && change.IsAfterSensitive() == change.IsBeforeSensitive() { return plans.NoOp } return plans.Update } -// getDefaultActionForIteration is used to guess what the change could be for +// GetDefaultActionForIteration is used to guess what the change could be for // complex attributes (collections and objects) and blocks. // // You can't really tell the difference between a NoOp and an Update just by @@ -192,7 +193,7 @@ func (change Change) calculateChange() plans.Action { // values were null, and returns a NoOp for all other cases. It should be used // in conjunction with compareActions to calculate the actual action based on // the actions of the children. -func (change Change) getDefaultActionForIteration() plans.Action { +func (change Change) GetDefaultActionForIteration() plans.Action { if change.Before == nil && change.After == nil { return plans.NoOp } diff --git a/internal/command/jsonformat/structured/doc.go b/internal/command/jsonformat/structured/doc.go new file mode 100644 index 0000000000..d3162bcae1 --- /dev/null +++ b/internal/command/jsonformat/structured/doc.go @@ -0,0 +1,6 @@ +// Package structured contains the structured representation of the JSON changes +// returned by the jsonplan package. +// +// Placing these in a dedicated package allows for greater reuse across the +// various type of renderers. +package structured diff --git a/internal/command/jsonformat/differ/change_map.go b/internal/command/jsonformat/structured/map.go similarity index 67% rename from internal/command/jsonformat/differ/change_map.go rename to internal/command/jsonformat/structured/map.go index 36ce500cfd..2e60fe47ec 100644 --- a/internal/command/jsonformat/differ/change_map.go +++ b/internal/command/jsonformat/structured/map.go @@ -1,7 +1,7 @@ -package differ +package structured import ( - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" ) // ChangeMap is a Change that represents a Map or an Object type, and has @@ -32,7 +32,10 @@ type ChangeMap struct { RelevantAttributes attribute_path.Matcher } -func (change Change) asMap() ChangeMap { +// AsMap converts the Change into an object or map representation by converting +// the internal Before, After, Unknown, BeforeSensitive, and AfterSensitive +// data structures into generic maps. +func (change Change) AsMap() ChangeMap { return ChangeMap{ Before: genericToMap(change.Before), After: genericToMap(change.After), @@ -44,7 +47,9 @@ func (change Change) asMap() ChangeMap { } } -func (m ChangeMap) getChild(key string) Change { +// GetChild safely packages up a Change object for the given child, handling +// all the cases where the data might be null or a static boolean. +func (m ChangeMap) GetChild(key string) Change { before, beforeExplicit := getFromGenericMap(m.Before, key) after, afterExplicit := getFromGenericMap(m.After, key) unknown, _ := getFromGenericMap(m.Unknown, key) @@ -64,6 +69,29 @@ func (m ChangeMap) getChild(key string) Change { } } +// Keys returns all the possible keys for this map. The keys for the map are +// potentially hidden and spread across multiple internal data structures and +// so this function conveniently packages them up. +func (m ChangeMap) Keys() []string { + var keys []string + for before := range m.Before { + keys = append(keys, before) + } + for after := range m.After { + keys = append(keys, after) + } + for unknown := range m.Unknown { + keys = append(keys, unknown) + } + for sensitive := range m.AfterSensitive { + keys = append(keys, sensitive) + } + for sensitive := range m.BeforeSensitive { + keys = append(keys, sensitive) + } + return keys +} + func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) { if generic == nil { return nil, false diff --git a/internal/command/jsonformat/structured/sensitive.go b/internal/command/jsonformat/structured/sensitive.go new file mode 100644 index 0000000000..99991887c2 --- /dev/null +++ b/internal/command/jsonformat/structured/sensitive.go @@ -0,0 +1,15 @@ +package structured + +func (change Change) IsBeforeSensitive() bool { + if sensitive, ok := change.BeforeSensitive.(bool); ok { + return sensitive + } + return false +} + +func (change Change) IsAfterSensitive() bool { + if sensitive, ok := change.AfterSensitive.(bool); ok { + return sensitive + } + return false +} diff --git a/internal/command/jsonformat/differ/change_slice.go b/internal/command/jsonformat/structured/slice.go similarity index 82% rename from internal/command/jsonformat/differ/change_slice.go rename to internal/command/jsonformat/structured/slice.go index 789b3e9844..ae7467d604 100644 --- a/internal/command/jsonformat/differ/change_slice.go +++ b/internal/command/jsonformat/structured/slice.go @@ -1,7 +1,7 @@ -package differ +package structured import ( - "github.com/hashicorp/terraform/internal/command/jsonformat/differ/attribute_path" + "github.com/hashicorp/terraform/internal/command/jsonformat/structured/attribute_path" ) // ChangeSlice is a Change that represents a Tuple, Set, or List type, and has @@ -31,7 +31,10 @@ type ChangeSlice struct { RelevantAttributes attribute_path.Matcher } -func (change Change) asSlice() ChangeSlice { +// AsSlice converts the Change into a slice representation by converting the +// internal Before, After, Unknown, BeforeSensitive, and AfterSensitive data +// structures into generic slices. +func (change Change) AsSlice() ChangeSlice { return ChangeSlice{ Before: genericToSlice(change.Before), After: genericToSlice(change.After), @@ -43,7 +46,9 @@ func (change Change) asSlice() ChangeSlice { } } -func (s ChangeSlice) getChild(beforeIx, afterIx int) Change { +// GetChild safely packages up a Change object for the given child, handling +// all the cases where the data might be null or a static boolean. +func (s ChangeSlice) GetChild(beforeIx, afterIx int) Change { before, beforeExplicit := getFromGenericSlice(s.Before, beforeIx) after, afterExplicit := getFromGenericSlice(s.After, afterIx) unknown, _ := getFromGenericSlice(s.Unknown, afterIx) diff --git a/internal/command/jsonformat/structured/unknown.go b/internal/command/jsonformat/structured/unknown.go new file mode 100644 index 0000000000..ad3f7bae23 --- /dev/null +++ b/internal/command/jsonformat/structured/unknown.go @@ -0,0 +1,8 @@ +package structured + +func (change Change) IsUnknown() bool { + if unknown, ok := change.Unknown.(bool); ok { + return unknown + } + return false +}