Skip to content

Commit d82f3be

Browse files
authored
QueryService: Use types from sdk (#84029)
1 parent f11b10a commit d82f3be

36 files changed

+1490
-814
lines changed

hack/update-codegen.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ grafana::codegen:run() {
2424
local generate_root=$1
2525
local skipped="true"
2626
for api_pkg in $(grafana:codegen:lsdirs ./${generate_root}/apis); do
27-
echo "Generating code for ${generate_root}/apis/${api_pkg}..."
28-
echo "============================================="
2927
if [[ "${selected_pkg}" != "" && ${api_pkg} != $selected_pkg ]]; then
3028
continue
3129
fi
30+
echo "Generating code for ${generate_root}/apis/${api_pkg}..."
31+
echo "============================================="
3232
skipped="false"
3333
include_common_input_dirs=$([[ ${api_pkg} == "common" ]] && echo "true" || echo "false")
3434

pkg/apis/query/v0alpha1/types.go pkg/apis/query/v0alpha1/datasource.go

-16
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,10 @@ package v0alpha1
33
import (
44
"context"
55

6-
"github.com/grafana/grafana-plugin-sdk-go/backend"
76
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
87
"k8s.io/apimachinery/pkg/runtime/schema"
98
)
109

11-
// The query runner interface
12-
type QueryRunner interface {
13-
// Runs the query as the user in context
14-
ExecuteQueryData(ctx context.Context,
15-
// The k8s group for the datasource (pluginId)
16-
datasource schema.GroupVersion,
17-
18-
// The datasource name/uid
19-
name string,
20-
21-
// The raw backend query objects
22-
query []GenericDataQuery,
23-
) (*backend.QueryDataResponse, error)
24-
}
25-
2610
type DataSourceApiServerRegistry interface {
2711
// Get the group and preferred version for a plugin
2812
GetDatasourceGroupVersion(pluginId string) (schema.GroupVersion, error)

pkg/apis/query/v0alpha1/query.go

+34-215
Original file line numberDiff line numberDiff line change
@@ -1,240 +1,59 @@
11
package v0alpha1
22

33
import (
4-
"encoding/json"
5-
"fmt"
4+
"net/http"
65

6+
"github.com/grafana/grafana-plugin-sdk-go/backend"
7+
data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8-
runtime "k8s.io/apimachinery/pkg/runtime"
99
)
1010

1111
// Generic query request with shared time across all values
1212
// 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 {
1415
metav1.TypeMeta `json:",inline"`
1516

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"`
9119
}
9220

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"`
9925

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"`
10628
}
10729

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
11234
}
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
12538
}
12639
}
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
15641
}
15742

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"`
16349

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+
}
22652

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"`
23757

238-
g.props = vals
239-
return nil
58+
Items []QueryTypeDefinition `json:"items,omitempty"`
24059
}

pkg/apis/query/v0alpha1/query_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import (
44
"encoding/json"
55
"testing"
66

7+
data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"
78
"github.com/stretchr/testify/require"
89

9-
"github.com/grafana/grafana/pkg/apis/query/v0alpha1"
10+
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
1011
)
1112

1213
func TestParseQueriesIntoQueryDataRequest(t *testing.T) {
@@ -39,23 +40,23 @@ func TestParseQueriesIntoQueryDataRequest(t *testing.T) {
3940
"to": "1692646267389"
4041
}`)
4142

42-
req := &v0alpha1.GenericQueryRequest{}
43+
req := &query.QueryDataRequest{}
4344
err := json.Unmarshal(request, req)
4445
require.NoError(t, err)
4546

4647
require.Len(t, req.Queries, 2)
4748
require.Equal(t, "b1808c48-9fc9-4045-82d7-081781f8a553", req.Queries[0].Datasource.UID)
48-
require.Equal(t, "spreadsheetID", req.Queries[0].AdditionalProperties()["spreadsheet"])
49+
require.Equal(t, "spreadsheetID", req.Queries[0].GetString("spreadsheet"))
4950

5051
// Write the query (with additional spreadsheetID) to JSON
5152
out, err := json.MarshalIndent(req.Queries[0], "", " ")
5253
require.NoError(t, err)
5354

5455
// And read it back with standard JSON marshal functions
55-
query := &v0alpha1.GenericDataQuery{}
56+
query := &data.DataQuery{}
5657
err = json.Unmarshal(out, query)
5758
require.NoError(t, err)
58-
require.Equal(t, "spreadsheetID", query.AdditionalProperties()["spreadsheet"])
59+
require.Equal(t, "spreadsheetID", query.GetString("spreadsheet"))
5960

6061
// The second query has an explicit time range, and legacy datasource name
6162
out, err = json.MarshalIndent(req.Queries[1], "", " ")

pkg/apis/query/v0alpha1/register.go

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ var DataSourceApiServerResourceInfo = common.NewResourceInfo(GROUP, VERSION,
1919
func() runtime.Object { return &DataSourceApiServerList{} },
2020
)
2121

22+
var QueryTypeDefinitionResourceInfo = common.NewResourceInfo(GROUP, VERSION,
23+
"querytypes", "querytype", "QueryTypeDefinition",
24+
func() runtime.Object { return &QueryTypeDefinition{} },
25+
func() runtime.Object { return &QueryTypeDefinitionList{} },
26+
)
27+
2228
var (
2329
// SchemeGroupVersion is group version used to register these objects
2430
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}

0 commit comments

Comments
 (0)