mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
Add support for outputs in the structured renderer (#32426)
* 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 * Add support for maps in the structured renderer * Add support for lists in the structured renderer * goimports * Add support for sets in the structured renderer * goimports * Add support for blocks in the structured renderer * goimports * Add support for outputs in the structured renderer * fix ordering of blocks * remove unused test stub * fix typo
This commit is contained in:
@@ -31,6 +31,10 @@ func (v Value) computeChangeForNestedAttribute(attribute *jsonprovider.NestedTyp
|
||||
}
|
||||
|
||||
func (v Value) computeChangeForType(ctyType cty.Type) change.Change {
|
||||
if ctyType == cty.NilType {
|
||||
return v.ComputeChangeForOutput()
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctyType.IsPrimitiveType():
|
||||
return v.computeAttributeChangeAsPrimitive(ctyType)
|
||||
|
||||
@@ -1,7 +1,86 @@
|
||||
package differ
|
||||
|
||||
import "github.com/hashicorp/terraform/internal/command/jsonformat/change"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/command/jsonformat/change"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonNumber = "number"
|
||||
jsonObject = "object"
|
||||
jsonArray = "array"
|
||||
jsonBool = "bool"
|
||||
jsonString = "string"
|
||||
jsonNull = "null"
|
||||
)
|
||||
|
||||
func (v Value) ComputeChangeForOutput() change.Change {
|
||||
panic("not implemented")
|
||||
beforeType := getJsonType(v.Before)
|
||||
afterType := getJsonType(v.After)
|
||||
|
||||
valueToAttribute := func(v Value, jsonType string) change.Change {
|
||||
var res change.Change
|
||||
|
||||
switch jsonType {
|
||||
case jsonNull:
|
||||
res = v.computeAttributeChangeAsPrimitive(cty.NilType)
|
||||
case jsonBool:
|
||||
res = v.computeAttributeChangeAsPrimitive(cty.Bool)
|
||||
case jsonString:
|
||||
res = v.computeAttributeChangeAsPrimitive(cty.String)
|
||||
case jsonNumber:
|
||||
res = v.computeAttributeChangeAsPrimitive(cty.Number)
|
||||
case jsonObject:
|
||||
res = v.computeAttributeChangeAsMap(cty.NilType)
|
||||
case jsonArray:
|
||||
res = v.computeAttributeChangeAsList(cty.NilType)
|
||||
default:
|
||||
panic("unrecognized json type: " + jsonType)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
if beforeType == afterType || (beforeType == jsonNull || afterType == jsonNull) {
|
||||
targetType := beforeType
|
||||
if targetType == jsonNull {
|
||||
targetType = afterType
|
||||
}
|
||||
return valueToAttribute(v, targetType)
|
||||
}
|
||||
|
||||
before := valueToAttribute(Value{
|
||||
Before: v.Before,
|
||||
BeforeSensitive: v.BeforeSensitive,
|
||||
}, beforeType)
|
||||
|
||||
after := valueToAttribute(Value{
|
||||
After: v.After,
|
||||
AfterSensitive: v.AfterSensitive,
|
||||
Unknown: v.Unknown,
|
||||
}, afterType)
|
||||
|
||||
return change.New(change.TypeChange(before, after), plans.Update, false)
|
||||
}
|
||||
|
||||
func getJsonType(json interface{}) string {
|
||||
switch json.(type) {
|
||||
case []interface{}:
|
||||
return jsonArray
|
||||
case float64:
|
||||
return jsonNumber
|
||||
case string:
|
||||
return jsonString
|
||||
case bool:
|
||||
return jsonBool
|
||||
case nil:
|
||||
return jsonNull
|
||||
case map[string]interface{}:
|
||||
return jsonObject
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized json type %T", json))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1208,6 +1208,231 @@ func TestValue_BlockAttributesAndNestedBlocks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValue_Outputs(t *testing.T) {
|
||||
tcs := map[string]struct {
|
||||
input Value
|
||||
validateChange change.ValidateChangeFunc
|
||||
}{
|
||||
"primitive_create": {
|
||||
input: Value{
|
||||
Before: nil,
|
||||
After: "new",
|
||||
},
|
||||
validateChange: change.ValidatePrimitive(nil, strptr("\"new\""), plans.Create, false),
|
||||
},
|
||||
"map_create": {
|
||||
input: Value{
|
||||
Before: nil,
|
||||
After: map[string]interface{}{
|
||||
"element_one": "new_one",
|
||||
"element_two": "new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
"element_two": change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false),
|
||||
},
|
||||
"list_create": {
|
||||
input: Value{
|
||||
Before: nil,
|
||||
After: []interface{}{
|
||||
"new_one",
|
||||
"new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false),
|
||||
},
|
||||
"primitive_update": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: "new",
|
||||
},
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), strptr("\"new\""), plans.Update, false),
|
||||
},
|
||||
"map_update": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"element_one": "old_one",
|
||||
"element_two": "old_two",
|
||||
},
|
||||
After: map[string]interface{}{
|
||||
"element_one": "new_one",
|
||||
"element_two": "new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(strptr("\"old_one\""), strptr("\"new_one\""), plans.Update, false),
|
||||
"element_two": change.ValidatePrimitive(strptr("\"old_two\""), strptr("\"new_two\""), plans.Update, false),
|
||||
}, plans.Update, false),
|
||||
},
|
||||
"list_update": {
|
||||
input: Value{
|
||||
Before: []interface{}{
|
||||
"old_one",
|
||||
"old_two",
|
||||
},
|
||||
After: []interface{}{
|
||||
"new_one",
|
||||
"new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Update, false),
|
||||
},
|
||||
"primitive_delete": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: nil,
|
||||
},
|
||||
validateChange: change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false),
|
||||
},
|
||||
"map_delete": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"element_one": "old_one",
|
||||
"element_two": "old_two",
|
||||
},
|
||||
After: nil,
|
||||
},
|
||||
validateChange: change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
"element_two": change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
},
|
||||
"list_delete": {
|
||||
input: Value{
|
||||
Before: []interface{}{
|
||||
"old_one",
|
||||
"old_two",
|
||||
},
|
||||
After: nil,
|
||||
},
|
||||
validateChange: change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
},
|
||||
"primitive_to_list": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: []interface{}{
|
||||
"new_one",
|
||||
"new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false),
|
||||
change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false), plans.Update, false),
|
||||
},
|
||||
"primitive_to_map": {
|
||||
input: Value{
|
||||
Before: "old",
|
||||
After: map[string]interface{}{
|
||||
"element_one": "new_one",
|
||||
"element_two": "new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidatePrimitive(strptr("\"old\""), nil, plans.Delete, false),
|
||||
change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
"element_two": change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false), plans.Update, false),
|
||||
},
|
||||
"list_to_primitive": {
|
||||
input: Value{
|
||||
Before: []interface{}{
|
||||
"old_one",
|
||||
"old_two",
|
||||
},
|
||||
After: "new",
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new\""), plans.Create, false),
|
||||
plans.Update, false),
|
||||
},
|
||||
"list_to_map": {
|
||||
input: Value{
|
||||
Before: []interface{}{
|
||||
"old_one",
|
||||
"old_two",
|
||||
},
|
||||
After: map[string]interface{}{
|
||||
"element_one": "new_one",
|
||||
"element_two": "new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
"element_two": change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false), plans.Update, false),
|
||||
},
|
||||
"map_to_primitive": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"element_one": "old_one",
|
||||
"element_two": "old_two",
|
||||
},
|
||||
After: "new",
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
"element_two": change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new\""), plans.Create, false),
|
||||
plans.Update, false),
|
||||
},
|
||||
"map_to_list": {
|
||||
input: Value{
|
||||
Before: map[string]interface{}{
|
||||
"element_one": "old_one",
|
||||
"element_two": "old_two",
|
||||
},
|
||||
After: []interface{}{
|
||||
"new_one",
|
||||
"new_two",
|
||||
},
|
||||
},
|
||||
validateChange: change.ValidateTypeChange(
|
||||
change.ValidateMap(map[string]change.ValidateChangeFunc{
|
||||
"element_one": change.ValidatePrimitive(strptr("\"old_one\""), nil, plans.Delete, false),
|
||||
"element_two": change.ValidatePrimitive(strptr("\"old_two\""), nil, plans.Delete, false),
|
||||
}, plans.Delete, false),
|
||||
change.ValidateList([]change.ValidateChangeFunc{
|
||||
change.ValidatePrimitive(nil, strptr("\"new_one\""), plans.Create, false),
|
||||
change.ValidatePrimitive(nil, strptr("\"new_two\""), plans.Create, false),
|
||||
}, plans.Create, false), plans.Update, false),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tcs {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tc.validateChange(t, tc.input.ComputeChange(cty.NilType))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValue_PrimitiveAttributes(t *testing.T) {
|
||||
// This function tests manipulating primitives: creating, deleting and
|
||||
// updating. It also automatically tests these operations within the
|
||||
|
||||
Reference in New Issue
Block a user