|
1 | 1 | package v0alpha1
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "encoding/json" |
5 |
| - "fmt" |
| 4 | + "net/http" |
6 | 5 |
|
| 6 | + "github.com/grafana/grafana-plugin-sdk-go/backend" |
| 7 | + data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1" |
7 | 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
8 |
| - runtime "k8s.io/apimachinery/pkg/runtime" |
9 | 9 | )
|
10 | 10 |
|
11 | 11 | // Generic query request with shared time across all values
|
12 | 12 | // Copied from: https://github.com/grafana/grafana/blob/main/pkg/api/dtos/models.go#L62
|
13 |
| -type GenericQueryRequest struct { |
| 13 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 14 | +type QueryDataRequest struct { |
14 | 15 | metav1.TypeMeta `json:",inline"`
|
15 | 16 |
|
16 |
| - // From Start time in epoch timestamps in milliseconds or relative using Grafana time units. |
17 |
| - // example: now-1h |
18 |
| - From string `json:"from,omitempty"` |
19 |
| - |
20 |
| - // To End time in epoch timestamps in milliseconds or relative using Grafana time units. |
21 |
| - // example: now |
22 |
| - To string `json:"to,omitempty"` |
23 |
| - |
24 |
| - // queries.refId – Specifies an identifier of the query. Is optional and default to “A”. |
25 |
| - // queries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId. |
26 |
| - // queries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100. |
27 |
| - // queries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000. |
28 |
| - // required: true |
29 |
| - // example: [ { "refId": "A", "intervalMs": 86400000, "maxDataPoints": 1092, "datasource":{ "uid":"PD8C576611E62080A" }, "rawSql": "SELECT 1 as valueOne, 2 as valueTwo", "format": "table" } ] |
30 |
| - Queries []GenericDataQuery `json:"queries"` |
31 |
| - |
32 |
| - // required: false |
33 |
| - Debug bool `json:"debug,omitempty"` |
34 |
| -} |
35 |
| - |
36 |
| -type DataSourceRef struct { |
37 |
| - // The datasource plugin type |
38 |
| - Type string `json:"type"` |
39 |
| - |
40 |
| - // Datasource UID |
41 |
| - UID string `json:"uid"` |
42 |
| -} |
43 |
| - |
44 |
| -// GenericDataQuery is a replacement for `dtos.MetricRequest` that provides more explicit types |
45 |
| -type GenericDataQuery struct { |
46 |
| - // RefID is the unique identifier of the query, set by the frontend call. |
47 |
| - RefID string `json:"refId"` |
48 |
| - |
49 |
| - // TimeRange represents the query range |
50 |
| - // NOTE: unlike generic /ds/query, we can now send explicit time values in each query |
51 |
| - TimeRange *TimeRange `json:"timeRange,omitempty"` |
52 |
| - |
53 |
| - // The datasource |
54 |
| - Datasource *DataSourceRef `json:"datasource,omitempty"` |
55 |
| - |
56 |
| - // Deprecated -- use datasource ref instead |
57 |
| - DatasourceId int64 `json:"datasourceId,omitempty"` |
58 |
| - |
59 |
| - // QueryType is an optional identifier for the type of query. |
60 |
| - // It can be used to distinguish different types of queries. |
61 |
| - QueryType string `json:"queryType,omitempty"` |
62 |
| - |
63 |
| - // MaxDataPoints is the maximum number of data points that should be returned from a time series query. |
64 |
| - MaxDataPoints int64 `json:"maxDataPoints,omitempty"` |
65 |
| - |
66 |
| - // Interval is the suggested duration between time points in a time series query. |
67 |
| - IntervalMS float64 `json:"intervalMs,omitempty"` |
68 |
| - |
69 |
| - // true if query is disabled (ie should not be returned to the dashboard) |
70 |
| - // Note this does not always imply that the query should not be executed since |
71 |
| - // the results from a hidden query may be used as the input to other queries (SSE etc) |
72 |
| - Hide bool `json:"hide,omitempty"` |
73 |
| - |
74 |
| - // Additional Properties (that live at the root) |
75 |
| - props map[string]any `json:"-"` |
76 |
| -} |
77 |
| - |
78 |
| -func NewGenericDataQuery(vals map[string]any) GenericDataQuery { |
79 |
| - q := GenericDataQuery{} |
80 |
| - _ = q.unmarshal(vals) |
81 |
| - return q |
82 |
| -} |
83 |
| - |
84 |
| -// TimeRange represents a time range for a query and is a property of DataQuery. |
85 |
| -type TimeRange struct { |
86 |
| - // From is the start time of the query. |
87 |
| - From string `json:"from"` |
88 |
| - |
89 |
| - // To is the end time of the query. |
90 |
| - To string `json:"to"` |
| 17 | + // The time range used when not included on each query |
| 18 | + data.QueryDataRequest `json:",inline"` |
91 | 19 | }
|
92 | 20 |
|
93 |
| -func (g *GenericDataQuery) AdditionalProperties() map[string]any { |
94 |
| - if g.props == nil { |
95 |
| - g.props = make(map[string]any) |
96 |
| - } |
97 |
| - return g.props |
98 |
| -} |
| 21 | +// Wraps backend.QueryDataResponse, however it includes TypeMeta and implements runtime.Object |
| 22 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 23 | +type QueryDataResponse struct { |
| 24 | + metav1.TypeMeta `json:",inline"` |
99 | 25 |
|
100 |
| -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. |
101 |
| -func (g *GenericDataQuery) DeepCopyInto(out *GenericDataQuery) { |
102 |
| - *out = *g |
103 |
| - if g.props != nil { |
104 |
| - out.props = runtime.DeepCopyJSON(g.props) |
105 |
| - } |
| 26 | + // Backend wrapper (external dependency) |
| 27 | + backend.QueryDataResponse `json:",inline"` |
106 | 28 | }
|
107 | 29 |
|
108 |
| -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericDataQuery. |
109 |
| -func (g *GenericDataQuery) DeepCopy() *GenericDataQuery { |
110 |
| - if g == nil { |
111 |
| - return nil |
| 30 | +// If errors exist, return multi-status |
| 31 | +func GetResponseCode(rsp *backend.QueryDataResponse) int { |
| 32 | + if rsp == nil { |
| 33 | + return http.StatusInternalServerError |
112 | 34 | }
|
113 |
| - out := new(GenericDataQuery) |
114 |
| - g.DeepCopyInto(out) |
115 |
| - return out |
116 |
| -} |
117 |
| - |
118 |
| -// MarshalJSON ensures that the unstructured object produces proper |
119 |
| -// JSON when passed to Go's standard JSON library. |
120 |
| -func (g GenericDataQuery) MarshalJSON() ([]byte, error) { |
121 |
| - vals := map[string]any{} |
122 |
| - if g.props != nil { |
123 |
| - for k, v := range g.props { |
124 |
| - vals[k] = v |
| 35 | + for _, v := range rsp.Responses { |
| 36 | + if v.Error != nil { |
| 37 | + return http.StatusMultiStatus |
125 | 38 | }
|
126 | 39 | }
|
127 |
| - |
128 |
| - vals["refId"] = g.RefID |
129 |
| - if g.Datasource != nil && (g.Datasource.Type != "" || g.Datasource.UID != "") { |
130 |
| - vals["datasource"] = g.Datasource |
131 |
| - } |
132 |
| - if g.DatasourceId > 0 { |
133 |
| - vals["datasourceId"] = g.DatasourceId |
134 |
| - } |
135 |
| - if g.IntervalMS > 0 { |
136 |
| - vals["intervalMs"] = g.IntervalMS |
137 |
| - } |
138 |
| - if g.MaxDataPoints > 0 { |
139 |
| - vals["maxDataPoints"] = g.MaxDataPoints |
140 |
| - } |
141 |
| - if g.QueryType != "" { |
142 |
| - vals["queryType"] = g.QueryType |
143 |
| - } |
144 |
| - return json.Marshal(vals) |
145 |
| -} |
146 |
| - |
147 |
| -// UnmarshalJSON ensures that the unstructured object properly decodes |
148 |
| -// JSON when passed to Go's standard JSON library. |
149 |
| -func (g *GenericDataQuery) UnmarshalJSON(b []byte) error { |
150 |
| - vals := map[string]any{} |
151 |
| - err := json.Unmarshal(b, &vals) |
152 |
| - if err != nil { |
153 |
| - return err |
154 |
| - } |
155 |
| - return g.unmarshal(vals) |
| 40 | + return http.StatusOK |
156 | 41 | }
|
157 | 42 |
|
158 |
| -func (g *GenericDataQuery) unmarshal(vals map[string]any) error { |
159 |
| - if vals == nil { |
160 |
| - g.props = nil |
161 |
| - return nil |
162 |
| - } |
| 43 | +// Defines a query behavior in a datasource. This is a similar model to a CRD where the |
| 44 | +// payload describes a valid query |
| 45 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 46 | +type QueryTypeDefinition struct { |
| 47 | + metav1.TypeMeta `json:",inline"` |
| 48 | + metav1.ObjectMeta `json:"metadata,omitempty"` |
163 | 49 |
|
164 |
| - key := "refId" |
165 |
| - v, ok := vals[key] |
166 |
| - if ok { |
167 |
| - g.RefID, ok = v.(string) |
168 |
| - if !ok { |
169 |
| - return fmt.Errorf("expected string refid (got: %t)", v) |
170 |
| - } |
171 |
| - delete(vals, key) |
172 |
| - } |
173 |
| - |
174 |
| - key = "datasource" |
175 |
| - v, ok = vals[key] |
176 |
| - if ok { |
177 |
| - wrap, ok := v.(map[string]any) |
178 |
| - if ok { |
179 |
| - g.Datasource = &DataSourceRef{} |
180 |
| - g.Datasource.Type, _ = wrap["type"].(string) |
181 |
| - g.Datasource.UID, _ = wrap["uid"].(string) |
182 |
| - delete(vals, key) |
183 |
| - } else { |
184 |
| - // Old old queries may arrive with just the name |
185 |
| - name, ok := v.(string) |
186 |
| - if !ok { |
187 |
| - return fmt.Errorf("expected datasource as object (got: %t)", v) |
188 |
| - } |
189 |
| - g.Datasource = &DataSourceRef{} |
190 |
| - g.Datasource.UID = name // Not great, but the lookup function will try its best to resolve |
191 |
| - delete(vals, key) |
192 |
| - } |
193 |
| - } |
194 |
| - |
195 |
| - key = "intervalMs" |
196 |
| - v, ok = vals[key] |
197 |
| - if ok { |
198 |
| - g.IntervalMS, ok = v.(float64) |
199 |
| - if !ok { |
200 |
| - return fmt.Errorf("expected intervalMs as float (got: %t)", v) |
201 |
| - } |
202 |
| - delete(vals, key) |
203 |
| - } |
204 |
| - |
205 |
| - key = "maxDataPoints" |
206 |
| - v, ok = vals[key] |
207 |
| - if ok { |
208 |
| - count, ok := v.(float64) |
209 |
| - if !ok { |
210 |
| - return fmt.Errorf("expected maxDataPoints as number (got: %t)", v) |
211 |
| - } |
212 |
| - g.MaxDataPoints = int64(count) |
213 |
| - delete(vals, key) |
214 |
| - } |
215 |
| - |
216 |
| - key = "datasourceId" |
217 |
| - v, ok = vals[key] |
218 |
| - if ok { |
219 |
| - count, ok := v.(float64) |
220 |
| - if !ok { |
221 |
| - return fmt.Errorf("expected datasourceId as number (got: %t)", v) |
222 |
| - } |
223 |
| - g.DatasourceId = int64(count) |
224 |
| - delete(vals, key) |
225 |
| - } |
| 50 | + Spec data.QueryTypeDefinitionSpec `json:"spec,omitempty"` |
| 51 | +} |
226 | 52 |
|
227 |
| - key = "queryType" |
228 |
| - v, ok = vals[key] |
229 |
| - if ok { |
230 |
| - queryType, ok := v.(string) |
231 |
| - if !ok { |
232 |
| - return fmt.Errorf("expected queryType as string (got: %t)", v) |
233 |
| - } |
234 |
| - g.QueryType = queryType |
235 |
| - delete(vals, key) |
236 |
| - } |
| 53 | +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object |
| 54 | +type QueryTypeDefinitionList struct { |
| 55 | + metav1.TypeMeta `json:",inline"` |
| 56 | + metav1.ListMeta `json:"metadata,omitempty"` |
237 | 57 |
|
238 |
| - g.props = vals |
239 |
| - return nil |
| 58 | + Items []QueryTypeDefinition `json:"items,omitempty"` |
240 | 59 | }
|
0 commit comments