blockToCtyValue ignores Valuable.Values() and may panic during reflection
- Dominant language
- Go
- Stars
- 3
- Forks
- 13
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
`golden.Valuable` declares a `Values() map[string]cty.Value` extension point, and Azure/grept already implements it for blocks such as `HttpDatasource` and `BaseFix`. However, `blockToCtyValue` currently always calls the reflection-based `Value(b)` and never consults `Valuable`.
This is more than an unused customization: reflection can panic before a block has a chance to provide its valid public cty representation. A common case is a decoded nested-block slice whose elements contain objects with different shapes.
Affected code on current `main` (`6e9a3fc2760e6f8440e4dfe8d0b1360886588bdd`):
```go
func blockToCtyValue(b Block) cty.Value {
blockValues := map[string]cty.Value{}
baseCtyValues := b.BaseValues()
ctyValues := Value(b)
// ...
}
```
Related grept usage:
- https://github.com/Azure/grept/blob/main/pkg/data_http.go
- https://github.com/Azure/grept/blob/main/pkg/fix.go
## Reproduction
Add the following declarations and test to `block_test.go`. It uses the test package's existing `BaseData` and Golden's `BaseBlock`:
```go
var _ Valuable = (*ValuableData)(nil)
type ValuableData struct {
*BaseData
*BaseBlock
ReflectedValues []cty.Value `hcl:"reflected_values"`
}
func (d *ValuableData) Type() string {
return "valuable"
}
func (d *ValuableData) ExecuteDuringPlan() error {
return nil
}
func (d *ValuableData) Values() map[string]cty.Value {
return map[string]cty.Value{
"custom_value": cty.StringVal("from Values"),
}
}
func TestBlockToCtyValueUsesValuableValues(t *testing.T) {
block := &ValuableData{
BaseData: &BaseData{},
BaseBlock: &BaseBlock{id: "block-id"},
ReflectedValues: []cty.Value{
cty.ObjectVal(map[string]cty.Value{"first": cty.True}),
cty.ObjectVal(map[string]cty.Value{"second": cty.True}),
},
}
value := blockToCtyValue(block)
assert.Equal(t, "from Values", value.GetAttr("custom_value").AsString())
assert.False(t, value.Type().HasAttribute("reflected_values"))
assert.Equal(t, "block-id", value.GetAttr("id").AsString())
}
```
Run:
```text
go test ./... -run TestBlockToCtyValueUsesValuableValues -count=1
```
## Actual behavior
The test panics inside `Value(b)` / `ToCtyValue` before `Values()` can be used:
```text
panic: inconsistent list element types
(cty.Object(map[string]cty.Type{"first":cty.Bool})
then cty.Object(map[string]cty.Type{"second":cty.Bool}))
```
## Expected behavior
When a block implements `Valuable`, Golden should use `Valuable.Values()` as the block's complete public cty representation and should not reflect that block. `BaseValues()` should still be merged afterward so Golden-owned metadata such as `id` remains available and keeps its existing precedence.
## Proposed fix
Make reflection and `Valuable` mutually exclusive:
```go
ctyValues := map[string]cty.Value{}
if valuable, ok := b.(Valuable); ok {
ctyValues = valuable.Values()
} else {
ctyValues = Value(b)
}
```
Then retain the existing merge order for `BaseValues()`.
It is important not to call `Value(b)` first and overwrite the result afterward: the reproduction demonstrates that reflection itself may panic, so late replacement does not solve the bug.
This should be backward compatible for blocks that do not implement `Valuable`, while making the already-declared interface usable as intended.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at blockToCtyValue and inspect how it currently combines Value(b) with BaseValues(). Add the reproduction and TestBlockToCtyValueUsesValuableValues in block_test.go, then run go test ./... -run TestBlockToCtyValueUsesValuableValues -count=1. Done means Valuable.Values() supplies the public representation without reflection, while the existing id metadata remains available with its current precedence.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 85/100