Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for TS.GET, TS.INFO and TS.QUERYINDEX commands #45

Merged
merged 2 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions pkg/data-frame.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"strconv"

"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
)

/**
* Create frame with single value
*
* @param {string} key Key
* @param {string} value Value
*/
func (ds *redisDatasource) createFrameValue(key string, value string) *data.Frame {
frame := data.NewFrame(key)

// Parse Float
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
frame.Fields = append(frame.Fields, data.NewField("Value", nil, []float64{floatValue}))
} else {
frame.Fields = append(frame.Fields, data.NewField("Value", nil, []string{value}))
}

// Return
return frame
}

/**
* Add Frame Fields from Array
*/
func (ds *redisDatasource) addFrameFieldsFromArray(values []interface{}, frame *data.Frame) *data.Frame {
for _, value := range values {
pair := value.([]interface{})
var key string

// Key
switch k := pair[0].(type) {
case []byte:
key = string(k)
default:
log.DefaultLogger.Error("addFrameFieldsFromArray", "Conversion Error", "Unsupported Key type")
}

// Value
switch v := pair[1].(type) {
case []byte:
value := string(v)

// Is it Integer?
if valueInt, err := strconv.ParseInt(value, 10, 64); err == nil {
frame.Fields = append(frame.Fields, data.NewField(key, nil, []int64{valueInt}))
break
}

// Add as string
frame.Fields = append(frame.Fields, data.NewField(key, nil, []string{value}))
case int64:
frame.Fields = append(frame.Fields, data.NewField(key, nil, []int64{v}))
default:
log.DefaultLogger.Error("addFrameFieldsFromArray", "Conversion Error", "Unsupported Value type")
}
}

return frame
}
4 changes: 2 additions & 2 deletions pkg/redis-custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
* Execute Query
* Can PANIC if command is wrong
*/
func (ds *redisDatasource) executeQuery(qm queryModel, client *radix.Pool) (interface{}, error) {
func (ds *redisDatasource) executeCustomQuery(qm queryModel, client *radix.Pool) (interface{}, error) {
// Split query and parse command
query := strings.Fields(qm.Query)
command, params := query[0], query[1:]
Expand Down Expand Up @@ -92,7 +92,7 @@ func (ds *redisDatasource) queryCustomCommand(qm queryModel, client *radix.Pool)
var err error

// Parse and execute query
result, err = ds.executeQuery(qm, client)
result, err = ds.executeCustomQuery(qm, client)

// Check error
if err != nil {
Expand Down
28 changes: 6 additions & 22 deletions pkg/redis-query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import (
"encoding/json"
"errors"
"fmt"
"strconv"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/mediocregopher/radix/v3"
"github.com/mediocregopher/radix/v3/resp/resp2"
)
Expand Down Expand Up @@ -53,6 +51,12 @@ func (ds *redisDatasource) query(ctx context.Context, query backend.DataQuery, c
* Commands switch
*/
switch qm.Command {
case "ts.get":
return ds.queryTsGet(qm, client)
case "ts.info":
return ds.queryTsInfo(qm, client)
case "ts.queryindex":
return ds.queryTsQueryIndex(qm, client)
case "ts.range":
return ds.queryTsRange(from, to, qm, client)
case "ts.mrange":
Expand Down Expand Up @@ -122,23 +126,3 @@ func (ds *redisDatasource) queryKeyCommand(qm queryModel, client *radix.Pool) ba
// Return Response
return response
}

/**
* Create frame with single value
*
* @param {string} key Key
* @param {string} value Value
*/
func (ds *redisDatasource) createFrameValue(key string, value string) *data.Frame {
frame := data.NewFrame(key)

// Parse Float
if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
frame.Fields = append(frame.Fields, data.NewField("Value", nil, []float64{floatValue}))
} else {
frame.Fields = append(frame.Fields, data.NewField("Value", nil, []string{value}))
}

// Return
return frame
}
134 changes: 134 additions & 0 deletions pkg/redis-time-series.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/mediocregopher/radix/v3"
)
Expand Down Expand Up @@ -170,3 +171,136 @@ func (ds *redisDatasource) queryTsMRange(from int64, to int64, qm queryModel, cl
// Return Response
return response
}

/**
* TS.GET key
*
* @see https://oss.redislabs.com/redistimeseries/1.4/commands/#tsget
*/
func (ds *redisDatasource) queryTsGet(qm queryModel, client *radix.Pool) backend.DataResponse {
response := backend.DataResponse{}

// Execute command
var result []string
err := client.Do(radix.Cmd(&result, qm.Command, qm.Key))

// Check error
if err != nil {
return ds.errorHandler(response, err)
}

// Create data frame response
frame := data.NewFrame(qm.Key,
data.NewField("time", nil, []time.Time{}),
data.NewField("value", nil, []float64{}))

// Add row
t, _ := strconv.ParseInt(result[0], 10, 64)
v, _ := strconv.ParseFloat(result[1], 64)
frame.AppendRow(time.Unix(t/1000, 0), v)

// Add the frame to the response
response.Frames = append(response.Frames, frame)

// Return Response
return response
}

/**
* TS.INFO key
*
* @see https://oss.redislabs.com/redistimeseries/1.4/commands/#tsinfo
*/
func (ds *redisDatasource) queryTsInfo(qm queryModel, client *radix.Pool) backend.DataResponse {
response := backend.DataResponse{}

// Execute command
var result interface{}
err := client.Do(radix.Cmd(&result, qm.Command, qm.Key))

// Check error
if err != nil {
return ds.errorHandler(response, err)
}

// Create data frame response
frame := data.NewFrame(qm.Key)

// Add fields and values
for i := 0; i < len(result.([]interface{})); i += 2 {

// Parameter
var param string
switch value := result.([]interface{})[i].(type) {
case string:
param = value
default:
log.DefaultLogger.Error("queryTsInfo", "Conversion Error", "Unsupported Key type")
}

// Value
switch value := result.([]interface{})[i+1].(type) {
case int64:
// Return timestamp as time
if param == "firstTimestamp" || param == "lastTimestamp" {
frame.Fields = append(frame.Fields, data.NewField(param, nil, []time.Time{time.Unix(value/1000, 0)}))
break
}

// Add field
field := data.NewField(param, nil, []int64{value})

// Set unit
if param == "memoryUsage" {
field.Config = &data.FieldConfig{Unit: "decbytes"}
} else if param == "retentionTime" {
field.Config = &data.FieldConfig{Unit: "ms"}
}

frame.Fields = append(frame.Fields, field)
case []byte:
frame.Fields = append(frame.Fields, data.NewField(param, nil, []string{string(value)}))
case []interface{}:
frame = ds.addFrameFieldsFromArray(value, frame)
default:
log.DefaultLogger.Error("queryTsInfo", "Conversion Error", "Unsupported Value type")
}
}

// Add the frame to the response
response.Frames = append(response.Frames, frame)

// Return Response
return response
}

/**
* TS.QUERYINDEX filter...
*
* @see https://oss.redislabs.com/redistimeseries/commands/#tsqueryindex
*/
func (ds *redisDatasource) queryTsQueryIndex(qm queryModel, client *radix.Pool) backend.DataResponse {
response := backend.DataResponse{}

// Split Filter to array
filter := strings.Fields(qm.Filter)

// Execute command
var values []string
err := client.Do(radix.Cmd(&values, qm.Command, filter...))

// Check error
if err != nil {
return ds.errorHandler(response, err)
}

// New Frame
frame := data.NewFrame(qm.Key,
data.NewField("Value", nil, values))

// Add the frames to the response
response.Frames = append(response.Frames, frame)

// Return
return response
}
19 changes: 18 additions & 1 deletion src/redis/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,26 @@ export const Commands = {
},
],
timeSeries: [
{
label: 'TS.GET',
description: 'Get the last sample',
value: 'ts.get',
},
{
label: 'TS.INFO',
description: 'Returns information and statistics on the time-series',
value: 'ts.info',
},
{
label: 'TS.MRANGE',
description: 'Query a timestamp range across multiple time-series by filters',
value: 'ts.mrange',
},
{
label: 'TS.QUERYINDEX',
description: 'Get all the keys matching the filter list',
value: 'ts.queryindex',
},
{ label: 'TS.RANGE', description: 'Query a range', value: 'ts.range' },
],
};
Expand All @@ -69,7 +84,7 @@ export const Commands = {
export const CommandParameters = {
aggregation: ['ts.range', 'ts.mrange'],
field: ['hget'],
filter: ['ts.mrange'],
filter: ['ts.mrange', 'ts.queryindex'],
key: [
'get',
'hget',
Expand All @@ -80,6 +95,8 @@ export const CommandParameters = {
'scard',
'smembers',
'ts.range',
'ts.get',
'ts.info',
'ttl',
'type',
'xinfoStream',
Expand Down