Add support for the replace paths data in the structured renderer (#32392)

* prep for processing the structured run output

* undo unwanted change to a json key

* Add skeleton functions and API for refactored renderer

* goimports

* Fix documentation of the RenderOpts struct

* Add rendering functionality for primitives to the structured renderer

* add test case for override

* Add support for parsing and rendering sensitive values in the renderer

* Add support for unknown/computed values in the structured renderer

* delete missing unit tests

* Add support for object attributes in the structured renderer

* goimports

* Add support for the replace paths data in the structured renderer
This commit is contained in:
Liam Cervante 2023-01-09 12:27:36 +01:00 committed by GitHub
parent 1eebcf875f
commit b097d8873d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 4 deletions

View File

@ -82,7 +82,7 @@ type Value struct {
// ReplacePaths generally contains nested slices that describe paths to
// elements or attributes that are causing the overall resource to be
// replaced.
ReplacePaths interface{}
ReplacePaths []interface{}
}
// ValueFromJsonChange unmarshals the raw []byte values in the jsonplan.Change
@ -94,7 +94,7 @@ func ValueFromJsonChange(change jsonplan.Change) Value {
Unknown: unmarshalGeneric(change.AfterUnknown),
BeforeSensitive: unmarshalGeneric(change.BeforeSensitive),
AfterSensitive: unmarshalGeneric(change.AfterSensitive),
ReplacePaths: unmarshalGeneric(change.ReplacePaths),
ReplacePaths: decodePaths(unmarshalGeneric(change.ReplacePaths)),
}
}
@ -129,8 +129,10 @@ func (v Value) AsChange(renderer change.Renderer) change.Change {
}
func (v Value) replacePath() bool {
if replace, ok := v.ReplacePaths.(bool); ok {
return replace
for _, path := range v.ReplacePaths {
if len(path.([]interface{})) == 0 {
return true
}
}
return false
}
@ -198,3 +200,10 @@ func unmarshalGeneric(raw json.RawMessage) interface{} {
}
return out
}
func decodePaths(paths interface{}) []interface{} {
if paths == nil {
return nil
}
return paths.([]interface{})
}

View File

@ -20,6 +20,9 @@ type ValueMap struct {
// AfterSensitive contains the after sensitive status of any
// elements/attributes of this map/object.
AfterSensitive map[string]interface{}
// ReplacePaths matches the same attributes in Value exactly.
ReplacePaths []interface{}
}
func (v Value) asMap() ValueMap {
@ -29,6 +32,7 @@ func (v Value) asMap() ValueMap {
Unknown: genericToMap(v.Unknown),
BeforeSensitive: genericToMap(v.BeforeSensitive),
AfterSensitive: genericToMap(v.AfterSensitive),
ReplacePaths: v.ReplacePaths,
}
}
@ -47,9 +51,26 @@ func (m ValueMap) getChild(key string) Value {
Unknown: unknown,
BeforeSensitive: beforeSensitive,
AfterSensitive: afterSensitive,
ReplacePaths: m.processReplacePaths(key),
}
}
func (m ValueMap) processReplacePaths(key string) []interface{} {
var ret []interface{}
for _, p := range m.ReplacePaths {
path := p.([]interface{})
if len(path) == 0 {
continue
}
if path[0].(string) == key {
ret = append(ret, path[1:])
}
}
return ret
}
func getFromGenericMap(generic map[string]interface{}, key string) (interface{}, bool) {
if generic == nil {
return nil, false

View File

@ -317,6 +317,48 @@ func TestValue_ObjectAttributes(t *testing.T) {
validateAction: plans.NoOp,
validateReplace: false,
},
"object_update_replace_self": {
input: Value{
Before: map[string]interface{}{
"attribute_one": "old",
},
After: map[string]interface{}{
"attribute_one": "new",
},
ReplacePaths: []interface{}{
[]interface{}{},
},
},
attributes: map[string]cty.Type{
"attribute_one": cty.String,
},
validateChanges: map[string]change.ValidateChangeFunc{
"attribute_one": change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, false),
},
validateAction: plans.Update,
validateReplace: true,
},
"object_update_replace_attribute": {
input: Value{
Before: map[string]interface{}{
"attribute_one": "old",
},
After: map[string]interface{}{
"attribute_one": "new",
},
ReplacePaths: []interface{}{
[]interface{}{"attribute_one"},
},
},
attributes: map[string]cty.Type{
"attribute_one": cty.String,
},
validateChanges: map[string]change.ValidateChangeFunc{
"attribute_one": change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, true),
},
validateAction: plans.Update,
validateReplace: false,
},
}
for name, tc := range tcs {
@ -484,6 +526,19 @@ func TestValue_Attribute(t *testing.T) {
},
validateChange: change.ValidateComputed(change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false), plans.Update, false),
},
"primitive_update_replace": {
input: Value{
Before: "old",
After: "new",
ReplacePaths: []interface{}{
[]interface{}{}, // An empty path suggests this attribute should be true.
},
},
attribute: &jsonprovider.Attribute{
AttributeType: unmarshalType(t, cty.String),
},
validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, true),
},
}
for name, tc := range tcs {
t.Run(name, func(t *testing.T) {