This repository has been archived by the owner on Jul 18, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
query.go
178 lines (154 loc) · 4.3 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package simplejson
import (
"encoding/json"
"errors"
"github.com/mailru/easyjson"
"strconv"
"time"
)
// QueryRequest is a Query request. For each specified Target, the server will call the appropriate handler's Query or TableQuery
// function with the provided QueryArgs.
//
//easyjson:skip
type QueryRequest struct {
Targets []Target `json:"targets"`
QueryArgs
}
// Target specifies the requested target name and type.
//
//easyjson:skip
type Target struct {
Name string `json:"target"` // name of the target.
Type string `json:"type"` // "timeserie" or "" for timeseries. "table" for table queries.
}
// QueryArgs contains the arguments for a Query.
//
//easyjson:skip
type QueryArgs struct {
Args
MaxDataPoints uint64 `json:"maxDataPoints"`
}
// UnmarshalJSON unmarshalls a QueryRequest from JSON
func (r *QueryRequest) UnmarshalJSON(b []byte) (err error) {
// workaround to avoid infinite loop
type Request2 QueryRequest
var c Request2
err = json.Unmarshal(b, &c)
if err == nil {
*r = QueryRequest(c)
}
return err
}
// TimeSeriesResponse is the response from a timeseries Query.
type TimeSeriesResponse struct {
Target string `json:"target"`
DataPoints []DataPoint `json:"datapoints"`
}
// DataPoint contains one entry returned by a Query.
//
//easyjson:skip
type DataPoint struct {
Timestamp time.Time
Value float64
}
// MarshalJSON converts a DataPoint to JSON.
func (d DataPoint) MarshalJSON() ([]byte, error) {
return []byte(`[` +
strconv.FormatFloat(d.Value, 'f', -1, 64) + `,` +
strconv.FormatInt(d.Timestamp.UnixMilli(), 10) +
`]`),
nil
}
// TableResponse is returned by a TableQuery, i.e. a slice of Column structures.
//
//easyjson:skip
type TableResponse struct {
Columns []Column
}
// Column is a column returned by a TableQuery. Text holds the column's header,
// Data holds the slice of values and should be a TimeColumn, a StringColumn
// or a NumberColumn.
type Column struct {
Text string
Data interface{}
}
// TimeColumn holds a slice of time.Time values (one per row).
type TimeColumn []time.Time
// StringColumn holds a slice of string values (one per row).
type StringColumn []string
// NumberColumn holds a slice of number values (one per row).
type NumberColumn []float64
type tableResponse struct {
Type string `json:"type"`
Columns []tableResponseColumn `json:"columns"`
Rows []tableResponseRow `json:"rows"`
}
type tableResponseColumn struct {
Text string `json:"text"`
Type string `json:"type"`
}
type tableResponseRow []interface{}
// MarshalJSON converts a TableResponse to JSON.
func (t TableResponse) MarshalJSON() (output []byte, err error) {
var colTypes []string
var rowCount int
if colTypes, rowCount, err = t.getColumnDetails(); err == nil {
output, err = easyjson.Marshal(tableResponse{
Type: "table",
Columns: t.buildColumns(colTypes),
Rows: t.buildRows(rowCount),
})
}
return output, err
}
func (t TableResponse) getColumnDetails() (colTypes []string, rowCount int, err error) {
for _, entry := range t.Columns {
var dataCount int
switch data := entry.Data.(type) {
case TimeColumn:
colTypes = append(colTypes, "time")
dataCount = len(data)
case StringColumn:
colTypes = append(colTypes, "string")
dataCount = len(data)
case NumberColumn:
colTypes = append(colTypes, "number")
dataCount = len(data)
}
if rowCount == 0 {
rowCount = dataCount
}
if dataCount != rowCount {
return colTypes, rowCount, errors.New("error building table query output: all columns must have the same number of rows")
}
}
return
}
func (t TableResponse) buildColumns(colTypes []string) []tableResponseColumn {
columns := make([]tableResponseColumn, len(colTypes))
for index, colType := range colTypes {
columns[index] = tableResponseColumn{
Text: t.Columns[index].Text,
Type: colType,
}
}
return columns
}
func (t TableResponse) buildRows(rowCount int) []tableResponseRow {
rows := make([]tableResponseRow, rowCount)
for row := 0; row < rowCount; row++ {
newRow := make(tableResponseRow, len(t.Columns))
for column, entry := range t.Columns {
switch data := entry.Data.(type) {
case TimeColumn:
newRow[column] = data[row]
case StringColumn:
newRow[column] = data[row]
case NumberColumn:
newRow[column] = data[row]
}
}
rows[row] = newRow
}
return rows
}