mirror of https://github.com/grafana/grafana.git
				
				
				
			SSE: Change math expression to accept any value convertible to float (#34996)
* SSE: Change math expression to accept any scalar value * Apply suggestions from code review * Update test * Remove TODO
This commit is contained in:
		
							parent
							
								
									b558d32502
								
							
						
					
					
						commit
						eeb84d09c2
					
				
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -52,7 +52,7 @@ require (
 | 
				
			||||||
	github.com/gosimple/slug v1.9.0
 | 
						github.com/gosimple/slug v1.9.0
 | 
				
			||||||
	github.com/grafana/grafana-aws-sdk v0.4.0
 | 
						github.com/grafana/grafana-aws-sdk v0.4.0
 | 
				
			||||||
	github.com/grafana/grafana-live-sdk v0.0.6
 | 
						github.com/grafana/grafana-live-sdk v0.0.6
 | 
				
			||||||
	github.com/grafana/grafana-plugin-sdk-go v0.102.0
 | 
						github.com/grafana/grafana-plugin-sdk-go v0.104.0
 | 
				
			||||||
	github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103
 | 
						github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103
 | 
				
			||||||
	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
 | 
						github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
 | 
				
			||||||
	github.com/hashicorp/go-hclog v0.16.0
 | 
						github.com/hashicorp/go-hclog v0.16.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										2
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -922,6 +922,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.79.0/go.mod h1:NvxLzGkVhnoBKwzkst6CF
 | 
				
			||||||
github.com/grafana/grafana-plugin-sdk-go v0.91.0/go.mod h1:Ot3k7nY7P6DXmUsDgKvNB7oG1v7PRyTdmnYVoS554bU=
 | 
					github.com/grafana/grafana-plugin-sdk-go v0.91.0/go.mod h1:Ot3k7nY7P6DXmUsDgKvNB7oG1v7PRyTdmnYVoS554bU=
 | 
				
			||||||
github.com/grafana/grafana-plugin-sdk-go v0.102.0 h1:Pknh7mlOaJvdhPgKHxcimDOSd9h29eSpA34W0/sOF6c=
 | 
					github.com/grafana/grafana-plugin-sdk-go v0.102.0 h1:Pknh7mlOaJvdhPgKHxcimDOSd9h29eSpA34W0/sOF6c=
 | 
				
			||||||
github.com/grafana/grafana-plugin-sdk-go v0.102.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
 | 
					github.com/grafana/grafana-plugin-sdk-go v0.102.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
 | 
				
			||||||
 | 
					github.com/grafana/grafana-plugin-sdk-go v0.104.0 h1:Ij2tPdEasSjCb2MxHaaiylyW4RLVZYyWpApzN/mlTxo=
 | 
				
			||||||
 | 
					github.com/grafana/grafana-plugin-sdk-go v0.104.0/go.mod h1:D7x3ah+1d4phNXpbnOaxa/osSaZlwh9/ZUnGGzegRbk=
 | 
				
			||||||
github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103 h1:qCmofFVwQR9QnsinstVqI1NPLMVl33jNCnOCXEAVn6E=
 | 
					github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103 h1:qCmofFVwQR9QnsinstVqI1NPLMVl33jNCnOCXEAVn6E=
 | 
				
			||||||
github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103/go.mod h1:GHIsn+EohCChsdu5YouNZewqLeV9L2FNw4DEJU3P9qE=
 | 
					github.com/grafana/loki v1.6.2-0.20210520072447-15d417efe103/go.mod h1:GHIsn+EohCChsdu5YouNZewqLeV9L2FNw4DEJU3P9qE=
 | 
				
			||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 | 
					github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,10 +30,27 @@ func makeNumber(name string, labels data.Labels, f *float64) Number {
 | 
				
			||||||
	return newNumber
 | 
						return newNumber
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func unixTimePointer(sec, nsec int64) *time.Time {
 | 
				
			||||||
 | 
						t := time.Unix(sec, nsec)
 | 
				
			||||||
 | 
						return &t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func float64Pointer(f float64) *float64 {
 | 
					func float64Pointer(f float64) *float64 {
 | 
				
			||||||
	return &f
 | 
						return &f
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func strPointer(s string) *string {
 | 
				
			||||||
 | 
						return &s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func int64Pointer(i int64) *int64 {
 | 
				
			||||||
 | 
						return &i
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func boolPointer(b bool) *bool {
 | 
				
			||||||
 | 
						return &b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var aSeries = Vars{
 | 
					var aSeries = Vars{
 | 
				
			||||||
	"A": Results{
 | 
						"A": Results{
 | 
				
			||||||
		[]Value{
 | 
							[]Value{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,8 +18,6 @@ const seriesTypeValIdx = 1
 | 
				
			||||||
// Series has a time.Time and a *float64 fields.
 | 
					// Series has a time.Time and a *float64 fields.
 | 
				
			||||||
type Series struct {
 | 
					type Series struct {
 | 
				
			||||||
	Frame *data.Frame
 | 
						Frame *data.Frame
 | 
				
			||||||
	// TODO:
 | 
					 | 
				
			||||||
	// - Value can be different number types
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SeriesFromFrame validates that the dataframe can be considered a Series type
 | 
					// SeriesFromFrame validates that the dataframe can be considered a Series type
 | 
				
			||||||
| 
						 | 
					@ -49,6 +47,26 @@ FIELDS:
 | 
				
			||||||
			valueNullable = true
 | 
								valueNullable = true
 | 
				
			||||||
			valueIdx = i
 | 
								valueIdx = i
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
 | 
								// Handle default case
 | 
				
			||||||
 | 
								// try to convert to *float64
 | 
				
			||||||
 | 
								var convertedField *data.Field
 | 
				
			||||||
 | 
								for j := 0; j < field.Len(); j++ {
 | 
				
			||||||
 | 
									ff, err := field.NullableFloatAt(j)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if convertedField == nil { // initialise field
 | 
				
			||||||
 | 
										convertedField = data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, field.Len())
 | 
				
			||||||
 | 
										convertedField.Name = field.Name
 | 
				
			||||||
 | 
										convertedField.Labels = field.Labels
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									convertedField.Set(j, ff)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if convertedField != nil {
 | 
				
			||||||
 | 
									frame.Fields[i] = convertedField
 | 
				
			||||||
 | 
									valueNullable = true
 | 
				
			||||||
 | 
									valueIdx = i
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if valueIdx != -1 && timeIdx != -1 {
 | 
								if valueIdx != -1 && timeIdx != -1 {
 | 
				
			||||||
				break FIELDS
 | 
									break FIELDS
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -65,7 +83,7 @@ FIELDS:
 | 
				
			||||||
	if timeNullable { // make time not nullable if it is in the input
 | 
						if timeNullable { // make time not nullable if it is in the input
 | 
				
			||||||
		timeSlice := make([]time.Time, 0, frame.Fields[timeIdx].Len())
 | 
							timeSlice := make([]time.Time, 0, frame.Fields[timeIdx].Len())
 | 
				
			||||||
		for rowIdx := 0; rowIdx < frame.Fields[timeIdx].Len(); rowIdx++ {
 | 
							for rowIdx := 0; rowIdx < frame.Fields[timeIdx].Len(); rowIdx++ {
 | 
				
			||||||
			val, ok := frame.At(0, rowIdx).(*time.Time)
 | 
								val, ok := frame.At(timeIdx, rowIdx).(*time.Time)
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				return s, fmt.Errorf("unexpected time type, expected *time.Time but got %T", val)
 | 
									return s, fmt.Errorf("unexpected time type, expected *time.Time but got %T", val)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,6 +136,132 @@ func TestSeriesFromFrame(t *testing.T) {
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]*int, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []*int64{int64Pointer(5)}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(5)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]int, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []int64{5}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(5)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]string, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []string{"5"}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(5)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]*string, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []*string{strPointer("5")}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(5)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]bool, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []bool{true}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(1)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "[]*bool, []*time frame should convert",
 | 
				
			||||||
 | 
								frame: &data.Frame{
 | 
				
			||||||
 | 
									Name: "test",
 | 
				
			||||||
 | 
									Fields: []*data.Field{
 | 
				
			||||||
 | 
										data.NewField("time", nil, []*time.Time{unixTimePointer(5, 0)}),
 | 
				
			||||||
 | 
										data.NewField("value", nil, []*bool{boolPointer(true)}),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								errIs: assert.NoError,
 | 
				
			||||||
 | 
								Is:    assert.Equal,
 | 
				
			||||||
 | 
								Series: Series{
 | 
				
			||||||
 | 
									Frame: &data.Frame{
 | 
				
			||||||
 | 
										Name: "test",
 | 
				
			||||||
 | 
										Fields: []*data.Field{
 | 
				
			||||||
 | 
											data.NewField("time", nil, []time.Time{time.Unix(5, 0)}),
 | 
				
			||||||
 | 
											data.NewField("value", nil, []*float64{float64Pointer(1)}),
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "[]*time, []*time frame should error",
 | 
								name: "[]*time, []*time frame should error",
 | 
				
			||||||
			frame: &data.Frame{
 | 
								frame: &data.Frame{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue