Compare commits

..

6 Commits

Author SHA1 Message Date
Andrew Nester 71bc701eed
fixes 2024-10-11 16:30:37 +02:00
Andrew Nester 82d6640170
removed path changes 2024-10-11 14:56:01 +02:00
Andrew Nester d4a334696e
fixed key location 2024-10-11 14:41:59 +02:00
Andrew Nester d25d95b70c
use dynassert 2024-10-11 13:01:06 +02:00
Andrew Nester 35f2d58180
fix test 2024-10-11 12:47:15 +02:00
Andrew Nester cf2ab73903
fixes 2024-10-11 12:17:58 +02:00
6 changed files with 54 additions and 42 deletions

View File

@ -263,34 +263,11 @@ func Render(ctx context.Context, v any) error {
return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) return renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, c.headerTemplate, c.template)
} }
func RenderWithDiagnostics(ctx context.Context, v any, diags diag.Diagnostics) error {
c := fromContext(ctx)
if _, ok := v.(listingInterface); ok {
panic("use RenderIterator instead")
}
err := renderWithTemplate(newRenderer(v), ctx, c.outputFormat, c.out, c.headerTemplate, c.template)
if err != nil {
return err
}
return RenderDiagnostics(c.out, diags)
}
func RenderIterator[T any](ctx context.Context, i listing.Iterator[T]) error { func RenderIterator[T any](ctx context.Context, i listing.Iterator[T]) error {
c := fromContext(ctx) c := fromContext(ctx)
return renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, c.headerTemplate, c.template) return renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, c.headerTemplate, c.template)
} }
func RenderIteratorWithDiagnostics[T any](ctx context.Context, i listing.Iterator[T], diags diag.Diagnostics) error {
c := fromContext(ctx)
err := renderWithTemplate(newIteratorRenderer(i), ctx, c.outputFormat, c.out, c.headerTemplate, c.template)
if err != nil {
return err
}
return RenderDiagnostics(c.err, diags)
}
func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template string) error { func RenderWithTemplate(ctx context.Context, v any, headerTemplate, template string) error {
c := fromContext(ctx) c := fromContext(ctx)
if _, ok := v.(listingInterface); ok { if _, ok := v.(listingInterface); ok {

View File

@ -248,7 +248,9 @@ func toTypedInt(dst reflect.Value, src dyn.Value) error {
return nil return nil
case dyn.KindFloat: case dyn.KindFloat:
v := src.MustFloat() v := src.MustFloat()
if canConvertToInt(v) { if v == float64(int64(v)) {
// If the destination is smaller than int64, but the value to set is bigger
// then destination overflows and is set to -1
dst.SetInt(int64(src.MustFloat())) dst.SetInt(int64(src.MustFloat()))
return nil return nil
} }
@ -275,10 +277,6 @@ func toTypedInt(dst reflect.Value, src dyn.Value) error {
} }
} }
func canConvertToInt(v float64) bool {
return v == float64(int64(v))
}
func toTypedFloat(dst reflect.Value, src dyn.Value) error { func toTypedFloat(dst reflect.Value, src dyn.Value) error {
switch src.Kind() { switch src.Kind() {
case dyn.KindFloat: case dyn.KindFloat:

View File

@ -34,13 +34,19 @@ func decodeValue(decoder *json.Decoder, o *Offset) (dyn.Value, error) {
return dyn.InvalidValue, err return dyn.InvalidValue, err
} }
// Get the current byte offset // Get the current byte offset and the location.
// We will later use this location to store the location of the value in the file
// For objects and arrays, we will store the location of the opening '{' or '['
// For primitive types, we will store the location of the value itself (end of the value)
// We can't reliably calculate the beginning of the value for primitive types because
// the decoder doesn't provide the offset of the beginning of the value and the value might or might not be quoted.
offset := decoder.InputOffset() offset := decoder.InputOffset()
location := o.GetPosition(offset) location := o.GetPosition(offset)
switch tok := token.(type) { switch tok := token.(type) {
case json.Delim: case json.Delim:
if tok == '{' { if tok == '{' {
location = o.GetPosition(offset - 1)
// Decode JSON object // Decode JSON object
obj := dyn.NewMapping() obj := dyn.NewMapping()
for decoder.More() { for decoder.More() {
@ -54,7 +60,10 @@ func decodeValue(decoder *json.Decoder, o *Offset) (dyn.Value, error) {
return invalidValueWithLocation(decoder, o), fmt.Errorf("expected string for object key") return invalidValueWithLocation(decoder, o), fmt.Errorf("expected string for object key")
} }
keyVal := dyn.NewValue(key, []dyn.Location{o.GetPosition(decoder.InputOffset())}) // Get the offset of the key by subtracting the length of the key and the '"' character
keyOffset := decoder.InputOffset() - int64(len(key)+1)
keyVal := dyn.NewValue(key, []dyn.Location{o.GetPosition(keyOffset)})
// Decode the value recursively // Decode the value recursively
val, err := decodeValue(decoder, o) val, err := decodeValue(decoder, o)
if err != nil { if err != nil {
@ -69,6 +78,7 @@ func decodeValue(decoder *json.Decoder, o *Offset) (dyn.Value, error) {
} }
return dyn.NewValue(obj, []dyn.Location{location}), nil return dyn.NewValue(obj, []dyn.Location{location}), nil
} else if tok == '[' { } else if tok == '[' {
location = o.GetPosition(offset - 1)
// Decode JSON array // Decode JSON array
var arr []dyn.Value var arr []dyn.Value
for decoder.More() { for decoder.More() {
@ -85,7 +95,6 @@ func decodeValue(decoder *json.Decoder, o *Offset) (dyn.Value, error) {
return dyn.NewValue(arr, []dyn.Location{location}), nil return dyn.NewValue(arr, []dyn.Location{location}), nil
} }
default: default:
// Primitive types: string, number, bool, or null
return dyn.NewValue(tok, []dyn.Location{location}), nil return dyn.NewValue(tok, []dyn.Location{location}), nil
} }

View File

@ -4,8 +4,8 @@ import (
"testing" "testing"
"github.com/databricks/cli/libs/dyn/convert" "github.com/databricks/cli/libs/dyn/convert"
"github.com/databricks/cli/libs/dyn/dynassert"
"github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/jobs"
"github.com/stretchr/testify/require"
) )
const jsonData = ` const jsonData = `
@ -45,11 +45,11 @@ const jsonData = `
func TestJsonLoader(t *testing.T) { func TestJsonLoader(t *testing.T) {
v, err := LoadJSON([]byte(jsonData), "(inline)") v, err := LoadJSON([]byte(jsonData), "(inline)")
require.NoError(t, err) dynassert.NoError(t, err)
var r jobs.ResetJob var r jobs.ResetJob
err = convert.ToTyped(&r, v) err = convert.ToTyped(&r, v)
require.NoError(t, err) dynassert.NoError(t, err)
} }
const malformedMap = ` const malformedMap = `
@ -64,7 +64,7 @@ const malformedMap = `
func TestJsonLoaderMalformedMap(t *testing.T) { func TestJsonLoaderMalformedMap(t *testing.T) {
_, err := LoadJSON([]byte(malformedMap), "(inline)") _, err := LoadJSON([]byte(malformedMap), "(inline)")
require.ErrorContains(t, err, "error decoding JSON at (inline):6:16: invalid character ',' after object key") dynassert.ErrorContains(t, err, "error decoding JSON at (inline):6:16: invalid character ',' after object key")
} }
const malformedArray = ` const malformedArray = `
@ -78,7 +78,7 @@ const malformedArray = `
func TestJsonLoaderMalformedArray(t *testing.T) { func TestJsonLoaderMalformedArray(t *testing.T) {
_, err := LoadJSON([]byte(malformedArray), "path/to/file.json") _, err := LoadJSON([]byte(malformedArray), "path/to/file.json")
require.ErrorContains(t, err, "error decoding JSON at path/to/file.json:6:28: invalid character ']' looking for beginning of value") dynassert.ErrorContains(t, err, "error decoding JSON at path/to/file.json:6:28: invalid character ']' looking for beginning of value")
} }
const eofData = ` const eofData = `
@ -89,5 +89,5 @@ const eofData = `
func TestJsonLoaderEOF(t *testing.T) { func TestJsonLoaderEOF(t *testing.T) {
_, err := LoadJSON([]byte(eofData), "path/to/file.json") _, err := LoadJSON([]byte(eofData), "path/to/file.json")
require.ErrorContains(t, err, "unexpected end of JSON input") dynassert.ErrorContains(t, err, "unexpected end of JSON input")
} }

View File

@ -52,7 +52,7 @@ func (j *JsonFlag) Unmarshal(v any) diag.Diagnostics {
// It will convert all the values to the correct types. // It will convert all the values to the correct types.
// For example string literals for booleans and integers will be converted to the correct types. // For example string literals for booleans and integers will be converted to the correct types.
nv, diags := convert.Normalize(v, dv) nv, diags := convert.Normalize(v, dv)
if len(diags) > 0 { if diags.HasError() {
return diags return diags
} }
@ -60,17 +60,17 @@ func (j *JsonFlag) Unmarshal(v any) diag.Diagnostics {
// It will serialize all set data with the correct types. // It will serialize all set data with the correct types.
data, err := json.Marshal(nv.AsAny()) data, err := json.Marshal(nv.AsAny())
if err != nil { if err != nil {
return diag.FromErr(err) return diags.Extend(diag.FromErr(err))
} }
// Finally unmarshal the normalized data to the output. // Finally unmarshal the normalized data to the output.
// It will fill in the ForceSendFields field if the struct contains it. // It will fill in the ForceSendFields field if the struct contains it.
err = marshal.Unmarshal(data, v) err = marshal.Unmarshal(data, v)
if err != nil { if err != nil {
return diag.FromErr(err) return diags.Extend(diag.FromErr(err))
} }
return nil return diags
} }
func (j *JsonFlag) Type() string { func (j *JsonFlag) Type() string {

View File

@ -182,7 +182,7 @@ func TestJsonUnmarshalRequestMismatch(t *testing.T) {
{ {
File: "(inline)", File: "(inline)",
Line: 3, Line: 3,
Column: 15, Column: 6,
}, },
}, },
Paths: []dyn.Path{{}}, Paths: []dyn.Path{{}},
@ -222,6 +222,34 @@ const wrontTypeJsonData = `{
func TestJsonUnmarshalWrongTypeReportsCorrectLocation(t *testing.T) { func TestJsonUnmarshalWrongTypeReportsCorrectLocation(t *testing.T) {
var body JsonFlag var body JsonFlag
var r jobs.ResetJob
err := body.Set(`{
"job_id": [1, 2, 3]
}
`)
require.NoError(t, err)
diags := body.Unmarshal(&r)
assert.NoError(t, diags.Error())
assert.NotEmpty(t, diags)
assert.Contains(t, diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "expected int, found sequence",
Locations: []dyn.Location{
{
File: "(inline)",
Line: 2,
Column: 15,
},
},
Paths: []dyn.Path{dyn.NewPath(dyn.Key("job_id"))},
})
}
func TestJsonUnmarshalArrayInsteadOfIntReportsCorrectLocation(t *testing.T) {
var body JsonFlag
var r jobs.ResetJob var r jobs.ResetJob
err := body.Set(wrontTypeJsonData) err := body.Set(wrontTypeJsonData)
require.NoError(t, err) require.NoError(t, err)