mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #127838 from benluddy/unstructured-nested-number-as-float64
Add NestedNumberAsFloat64 unstructured field accessor.
This commit is contained in:
		@@ -125,6 +125,28 @@ func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, err
 | 
			
		||||
	return i, true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NestedNumberAsFloat64 returns the float64 value of a nested field. If the field's value is a
 | 
			
		||||
// float64, it is returned. If the field's value is an int64 that can be losslessly converted to
 | 
			
		||||
// float64, it will be converted and returned.  Returns false if value is not found and an error if
 | 
			
		||||
// not a float64 or an int64 that can be accurately represented as a float64.
 | 
			
		||||
func NestedNumberAsFloat64(obj map[string]interface{}, fields ...string) (float64, bool, error) {
 | 
			
		||||
	val, found, err := NestedFieldNoCopy(obj, fields...)
 | 
			
		||||
	if !found || err != nil {
 | 
			
		||||
		return 0, found, err
 | 
			
		||||
	}
 | 
			
		||||
	switch x := val.(type) {
 | 
			
		||||
	case int64:
 | 
			
		||||
		if x != int64(float64(x)) {
 | 
			
		||||
			return 0, false, fmt.Errorf("%v accessor error: int64 value %v cannot be losslessly converted to float64", jsonPath(fields), x)
 | 
			
		||||
		}
 | 
			
		||||
		return float64(x), true, nil
 | 
			
		||||
	case float64:
 | 
			
		||||
		return x, true, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, false, fmt.Errorf("%v accessor error: %v is of the type %T, expected float64 or int64", jsonPath(fields), val, val)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NestedStringSlice returns a copy of []string value of a nested field.
 | 
			
		||||
// Returns false if value is not found and an error if not a []interface{} or contains non-string items in the slice.
 | 
			
		||||
func NestedStringSlice(obj map[string]interface{}, fields ...string) ([]string, bool, error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ package unstructured
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
@@ -225,3 +226,74 @@ func TestSetNestedMap(t *testing.T) {
 | 
			
		||||
	assert.Len(t, obj["x"].(map[string]interface{})["z"], 1)
 | 
			
		||||
	assert.Equal(t, "bar", obj["x"].(map[string]interface{})["z"].(map[string]interface{})["b"])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNestedNumberAsFloat64(t *testing.T) {
 | 
			
		||||
	for _, tc := range []struct {
 | 
			
		||||
		name           string
 | 
			
		||||
		obj            map[string]interface{}
 | 
			
		||||
		path           []string
 | 
			
		||||
		wantFloat64    float64
 | 
			
		||||
		wantBool       bool
 | 
			
		||||
		wantErrMessage string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:           "not found",
 | 
			
		||||
			obj:            nil,
 | 
			
		||||
			path:           []string{"missing"},
 | 
			
		||||
			wantFloat64:    0,
 | 
			
		||||
			wantBool:       false,
 | 
			
		||||
			wantErrMessage: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "found float64",
 | 
			
		||||
			obj:            map[string]interface{}{"value": float64(42)},
 | 
			
		||||
			path:           []string{"value"},
 | 
			
		||||
			wantFloat64:    42,
 | 
			
		||||
			wantBool:       true,
 | 
			
		||||
			wantErrMessage: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "found unexpected type bool",
 | 
			
		||||
			obj:            map[string]interface{}{"value": true},
 | 
			
		||||
			path:           []string{"value"},
 | 
			
		||||
			wantFloat64:    0,
 | 
			
		||||
			wantBool:       false,
 | 
			
		||||
			wantErrMessage: ".value accessor error: true is of the type bool, expected float64 or int64",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "found int64",
 | 
			
		||||
			obj:            map[string]interface{}{"value": int64(42)},
 | 
			
		||||
			path:           []string{"value"},
 | 
			
		||||
			wantFloat64:    42,
 | 
			
		||||
			wantBool:       true,
 | 
			
		||||
			wantErrMessage: "",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "found int64 not representable as float64",
 | 
			
		||||
			obj:            map[string]interface{}{"value": int64(math.MaxInt64)},
 | 
			
		||||
			path:           []string{"value"},
 | 
			
		||||
			wantFloat64:    0,
 | 
			
		||||
			wantBool:       false,
 | 
			
		||||
			wantErrMessage: ".value accessor error: int64 value 9223372036854775807 cannot be losslessly converted to float64",
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			gotFloat64, gotBool, gotErr := NestedNumberAsFloat64(tc.obj, tc.path...)
 | 
			
		||||
			if gotFloat64 != tc.wantFloat64 {
 | 
			
		||||
				t.Errorf("got %v, wanted %v", gotFloat64, tc.wantFloat64)
 | 
			
		||||
			}
 | 
			
		||||
			if gotBool != tc.wantBool {
 | 
			
		||||
				t.Errorf("got %t, wanted %t", gotBool, tc.wantBool)
 | 
			
		||||
			}
 | 
			
		||||
			if tc.wantErrMessage != "" {
 | 
			
		||||
				if gotErr == nil {
 | 
			
		||||
					t.Errorf("got nil error, wanted %s", tc.wantErrMessage)
 | 
			
		||||
				} else if gotErrMessage := gotErr.Error(); gotErrMessage != tc.wantErrMessage {
 | 
			
		||||
					t.Errorf("wanted error %q, got: %v", gotErrMessage, tc.wantErrMessage)
 | 
			
		||||
				}
 | 
			
		||||
			} else if gotErr != nil {
 | 
			
		||||
				t.Errorf("wanted nil error, got %v", gotErr)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user