mirror of https://github.com/databricks/cli.git
Handle normalization of `dyn.KindTime` into an any type (#1836)
## Changes The issue reported in #1828 illustrates how using a YAML timestamp-like value (a date in this case) causes an issue during conversion to and from the typed configuration tree. We use the `AsAny()` function on the `dyn.Value` when normalizing for the `any` type. We only use the `any` type for variable values, because they can assume every type. The `AsAny()` function returns a `time.Time` for the time value during conversion **to** the typed configuration tree. Upon conversion **from** the typed configuration tree back into the dynamic configuration tree, we cannot distinguish a `time.Time` struct from any other struct. To address this, we use the underlying string value of the time value when we normalize for the `any` type. Fixes #1828. ## Tests Existing unit tests pass
This commit is contained in:
parent
cc112961ce
commit
e4d039a1aa
|
@ -0,0 +1,33 @@
|
||||||
|
bundle:
|
||||||
|
name: issue_1828
|
||||||
|
|
||||||
|
variables:
|
||||||
|
# One entry for each of the underlying YAML (or [dyn.Kind]) types.
|
||||||
|
# The test confirms we can convert to and from the typed configuration without losing information.
|
||||||
|
|
||||||
|
map:
|
||||||
|
default:
|
||||||
|
foo: bar
|
||||||
|
|
||||||
|
sequence:
|
||||||
|
default:
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
|
||||||
|
string:
|
||||||
|
default: foo
|
||||||
|
|
||||||
|
bool:
|
||||||
|
default: true
|
||||||
|
|
||||||
|
int:
|
||||||
|
default: 42
|
||||||
|
|
||||||
|
float:
|
||||||
|
default: 3.14
|
||||||
|
|
||||||
|
time:
|
||||||
|
default: 2021-01-01
|
||||||
|
|
||||||
|
nil:
|
||||||
|
default:
|
|
@ -0,0 +1,48 @@
|
||||||
|
package config_tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIssue1828(t *testing.T) {
|
||||||
|
b := load(t, "./issue_1828")
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "map") {
|
||||||
|
assert.Equal(t, map[string]any{
|
||||||
|
"foo": "bar",
|
||||||
|
}, b.Config.Variables["map"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "sequence") {
|
||||||
|
assert.Equal(t, []any{
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
}, b.Config.Variables["sequence"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "string") {
|
||||||
|
assert.Equal(t, "foo", b.Config.Variables["string"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "bool") {
|
||||||
|
assert.Equal(t, true, b.Config.Variables["bool"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "int") {
|
||||||
|
assert.Equal(t, 42, b.Config.Variables["int"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "float") {
|
||||||
|
assert.Equal(t, 3.14, b.Config.Variables["float"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "time") {
|
||||||
|
assert.Equal(t, "2021-01-01", b.Config.Variables["time"].Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
if assert.Contains(t, b.Config.Variables, "nil") {
|
||||||
|
assert.Equal(t, nil, b.Config.Variables["nil"].Default)
|
||||||
|
}
|
||||||
|
}
|
|
@ -398,6 +398,34 @@ func (n normalizeOptions) normalizeFloat(typ reflect.Type, src dyn.Value, path d
|
||||||
return dyn.NewValue(out, src.Locations()), diags
|
return dyn.NewValue(out, src.Locations()), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n normalizeOptions) normalizeInterface(typ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
func (n normalizeOptions) normalizeInterface(_ reflect.Type, src dyn.Value, path dyn.Path) (dyn.Value, diag.Diagnostics) {
|
||||||
|
// Deal with every [dyn.Kind] here to ensure completeness.
|
||||||
|
switch src.Kind() {
|
||||||
|
case dyn.KindMap:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindSequence:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindString:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindBool:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindInt:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindFloat:
|
||||||
|
// Fall through
|
||||||
|
case dyn.KindTime:
|
||||||
|
// Conversion of a time value to an interface{}.
|
||||||
|
// The [dyn.Value.AsAny] equivalent for this kind is the [time.Time] struct.
|
||||||
|
// If we convert to a typed representation and back again, we cannot distinguish
|
||||||
|
// a [time.Time] struct from any other struct.
|
||||||
|
//
|
||||||
|
// Therefore, we normalize the time value to a string.
|
||||||
|
return dyn.NewValue(src.MustTime().String(), src.Locations()), nil
|
||||||
|
case dyn.KindNil:
|
||||||
|
// Fall through
|
||||||
|
default:
|
||||||
|
return dyn.InvalidValue, diag.Errorf("unsupported kind: %s", src.Kind())
|
||||||
|
}
|
||||||
|
|
||||||
return src, nil
|
return src, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -858,23 +858,7 @@ func TestNormalizeAnchors(t *testing.T) {
|
||||||
}, vout.AsAny())
|
}, vout.AsAny())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeBoolToAny(t *testing.T) {
|
func TestNormalizeAnyFromSlice(t *testing.T) {
|
||||||
var typ any
|
|
||||||
vin := dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
|
||||||
vout, err := Normalize(&typ, vin)
|
|
||||||
assert.Len(t, err, 0)
|
|
||||||
assert.Equal(t, dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeIntToAny(t *testing.T) {
|
|
||||||
var typ any
|
|
||||||
vin := dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
|
||||||
vout, err := Normalize(&typ, vin)
|
|
||||||
assert.Len(t, err, 0)
|
|
||||||
assert.Equal(t, dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeSliceToAny(t *testing.T) {
|
|
||||||
var typ any
|
var typ any
|
||||||
v1 := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
v1 := dyn.NewValue(1, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
v2 := dyn.NewValue(2, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
v2 := dyn.NewValue(2, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
|
@ -883,3 +867,35 @@ func TestNormalizeSliceToAny(t *testing.T) {
|
||||||
assert.Len(t, err, 0)
|
assert.Len(t, err, 0)
|
||||||
assert.Equal(t, dyn.NewValue([]dyn.Value{v1, v2}, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
assert.Equal(t, dyn.NewValue([]dyn.Value{v1, v2}, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAnyFromString(t *testing.T) {
|
||||||
|
var typ any
|
||||||
|
vin := dyn.NewValue("string", []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
|
vout, err := Normalize(&typ, vin)
|
||||||
|
assert.Len(t, err, 0)
|
||||||
|
assert.Equal(t, dyn.NewValue("string", []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAnyFromBool(t *testing.T) {
|
||||||
|
var typ any
|
||||||
|
vin := dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
|
vout, err := Normalize(&typ, vin)
|
||||||
|
assert.Len(t, err, 0)
|
||||||
|
assert.Equal(t, dyn.NewValue(false, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAnyFromInt(t *testing.T) {
|
||||||
|
var typ any
|
||||||
|
vin := dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
|
vout, err := Normalize(&typ, vin)
|
||||||
|
assert.Len(t, err, 0)
|
||||||
|
assert.Equal(t, dyn.NewValue(10, []dyn.Location{{File: "file", Line: 1, Column: 1}}), vout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeAnyFromTime(t *testing.T) {
|
||||||
|
var typ any
|
||||||
|
vin := dyn.NewValue(dyn.MustTime("2024-08-29"), []dyn.Location{{File: "file", Line: 1, Column: 1}})
|
||||||
|
vout, err := Normalize(&typ, vin)
|
||||||
|
assert.Empty(t, err)
|
||||||
|
assert.Equal(t, dyn.NewValue("2024-08-29", vin.Locations()), vout)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue