Skip to content

Commit 6a9e340

Browse files
Merge pull request #178 from grafadruid/improvement/modernize-grafana-compatibility
Improvement/modernize grafana compatibility
2 parents 1bf2947 + fdd6dd5 commit 6a9e340

File tree

8 files changed

+103
-141
lines changed

8 files changed

+103
-141
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "druid-grafana",
3-
"version": "1.6.1",
3+
"version": "1.7.0",
44
"description": "Connects Grafana to Druid",
55
"scripts": {
66
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",

pkg/druid.go

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
druidquerybuilder "github.com/grafadruid/go-druid/builder"
2020
druidquery "github.com/grafadruid/go-druid/builder/query"
2121
"github.com/grafana/grafana-plugin-sdk-go/backend"
22-
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
2322
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
2423
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
2524
"github.com/grafana/grafana-plugin-sdk-go/data"
@@ -184,22 +183,23 @@ func mergeSettings(settings ...map[string]any) map[string]any {
184183
return stg
185184
}
186185

187-
func newDatasource() datasource.ServeOpts {
188-
ds := &druidDatasource{
189-
im: datasource.NewInstanceManager(func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
190-
return newDataSourceInstance(ctx, settings)
191-
}),
186+
func newDatasource(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
187+
inst, err := newDataSourceInstance(ctx, settings)
188+
if err != nil {
189+
return nil, err
192190
}
193191

194-
return datasource.ServeOpts{
195-
QueryDataHandler: ds,
196-
CheckHealthHandler: ds,
197-
CallResourceHandler: ds,
198-
}
192+
return &druidDatasource{
193+
settings: inst.(*druidInstanceSettings),
194+
}, nil
199195
}
200196

201197
type druidDatasource struct {
202-
im instancemgmt.InstanceManager
198+
settings *druidInstanceSettings
199+
}
200+
201+
func (ds *druidDatasource) Dispose() {
202+
ds.settings.Dispose()
203203
}
204204

205205
func (ds *druidDatasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
@@ -234,30 +234,26 @@ type grafanaMetricFindValue struct {
234234
}
235235

236236
func (ds *druidDatasource) QueryVariableData(ctx context.Context, req *backend.CallResourceRequest) ([]grafanaMetricFindValue, error) {
237-
log.DefaultLogger.Info("QUERY VARIABLE", "request", string(req.Body))
238-
s, err := ds.settings(ctx, req.PluginContext)
239-
if err != nil {
240-
return []grafanaMetricFindValue{}, err
241-
}
242-
return ds.queryVariable(req.Body, s)
237+
log.DefaultLogger.Debug("QUERY VARIABLE", "request", string(req.Body))
238+
return ds.queryVariable(req.Body, ds.settings)
243239
}
244240

245241
func (ds *druidDatasource) queryVariable(qry []byte, s *druidInstanceSettings) ([]grafanaMetricFindValue, error) {
246-
log.DefaultLogger.Info("DRUID EXECUTE QUERY VARIABLE", "grafana_query", string(qry))
242+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY VARIABLE", "grafana_query", string(qry))
247243
// feature: probably implement a short (1s ? 500ms ? configurable in datasource ? beware memory: constrain size ?) life cache (druidInstanceSettings.cache ?) and early return then
248244
response := []grafanaMetricFindValue{}
249245
q, stg, err := ds.prepareQuery(qry, s)
250246
if err != nil {
251247
return response, err
252248
}
253-
log.DefaultLogger.Info("DRUID EXECUTE QUERY VARIABLE", "druid_query", q)
249+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY VARIABLE", "druid_query", q)
254250
r, err := ds.executeQuery("variable", q, s, stg)
255251
if err != nil {
256252
return response, err
257253
}
258-
log.DefaultLogger.Info("DRUID EXECUTE QUERY VARIABLE", "druid_response", r)
254+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY VARIABLE", "druid_response", r)
259255
response, err = ds.prepareVariableResponse(r, stg)
260-
log.DefaultLogger.Info("DRUID EXECUTE QUERY VARIABLE", "grafana_response", response)
256+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY VARIABLE", "grafana_response", response)
261257
return response, err
262258
}
263259

@@ -329,13 +325,7 @@ func (ds *druidDatasource) CheckHealth(ctx context.Context, req *backend.CheckHe
329325
Message: "Can't connect to Druid",
330326
}
331327

332-
i, err := ds.im.Get(ctx, req.PluginContext)
333-
if err != nil {
334-
result.Message = "Can't get Druid instance: " + err.Error()
335-
return result, nil
336-
}
337-
338-
status, _, err := i.(*druidInstanceSettings).client.Common().Status()
328+
status, _, err := ds.settings.client.Common().Status()
339329
if err != nil {
340330
result.Message = "Can't fetch Druid status: " + err.Error()
341331
return result, nil
@@ -349,28 +339,15 @@ func (ds *druidDatasource) CheckHealth(ctx context.Context, req *backend.CheckHe
349339
func (ds *druidDatasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
350340
response := backend.NewQueryDataResponse()
351341

352-
s, err := ds.settings(ctx, req.PluginContext)
353-
if err != nil {
354-
return response, err
355-
}
356-
357342
for _, q := range req.Queries {
358-
response.Responses[q.RefID] = ds.query(q, s)
343+
response.Responses[q.RefID] = ds.query(q, ds.settings)
359344
}
360345

361346
return response, nil
362347
}
363348

364-
func (ds *druidDatasource) settings(ctx context.Context, pluginCtx backend.PluginContext) (*druidInstanceSettings, error) {
365-
s, err := ds.im.Get(ctx, pluginCtx)
366-
if err != nil {
367-
return nil, err
368-
}
369-
return s.(*druidInstanceSettings), nil
370-
}
371-
372349
func (ds *druidDatasource) query(qry backend.DataQuery, s *druidInstanceSettings) backend.DataResponse {
373-
log.DefaultLogger.Info("DRUID EXECUTE QUERY", "grafana_query", qry)
350+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY", "grafana_query", qry)
374351
rawQuery := interpolateVariables(string(qry.JSON), qry.Interval, qry.TimeRange.Duration())
375352

376353
// feature: probably implement a short (1s ? 500ms ? configurable in datasource ? beware memory: constrain size ?) life cache (druidInstanceSettings.cache ?) and early return then
@@ -380,19 +357,19 @@ func (ds *druidDatasource) query(qry backend.DataQuery, s *druidInstanceSettings
380357
response.Error = err
381358
return response
382359
}
383-
log.DefaultLogger.Info("DRUID EXECUTE QUERY", "druid_query", q)
360+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY", "druid_query", q)
384361
r, err := ds.executeQuery(qry.RefID, q, s, stg)
385362
if err != nil {
386363
response.Error = err
387364
return response
388365
}
389-
log.DefaultLogger.Info("DRUID EXECUTE QUERY", "druid_response", r)
366+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY", "druid_response", r)
390367
response, err = ds.prepareResponse(r, stg)
391368
if err != nil {
392369
// note: error could be set from prepareResponse but this gives a chance to react to error here
393370
response.Error = err
394371
}
395-
log.DefaultLogger.Info("DRUID EXECUTE QUERY", "grafana_response", response)
372+
log.DefaultLogger.Debug("DRUID EXECUTE QUERY", "grafana_response", response)
396373
return response
397374
}
398375

pkg/main.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import (
99

1010
func main() {
1111
// Start listening to requests sent from Grafana. This call is blocking so
12-
// it won't finish until Grafana shuts down the process. or the plugin chooses
13-
// to exit and close down by itself.
14-
// Log any error if we could not start the plugin.
15-
if err := datasource.Serve(newDatasource()); err != nil {
12+
// it won't finish until Grafana shuts down the process or the plugin choose
13+
// to exit by itself using os.Exit. Manage automatically manages life cycle
14+
// of datasource instances. It accepts datasource instance factory as first
15+
// argument. This factory will be automatically called on incoming request
16+
// from Grafana to create different instances of DruidDatasource (per datasource
17+
// ID). When datasource configuration changed Dispose method will be called and
18+
// new datasource instance created using NewDatasource factory.
19+
if err := datasource.Manage("grafadruid-druid-datasource", newDatasource, datasource.ManageOpts{}); err != nil {
1620
log.DefaultLogger.Error(err.Error())
1721
os.Exit(1)
1822
}

src/builder/abstract/DateInterval.tsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
import React, { ChangeEvent } from 'react';
2-
import { InlineLabel, stylesFactory, useTheme } from '@grafana/ui';
3-
import { GrafanaTheme } from '@grafana/data';
2+
import { InlineLabel, useStyles2 } from '@grafana/ui';
3+
import { GrafanaTheme2 } from '@grafana/data';
44
import { QueryBuilderFieldProps } from './types';
55
import { onBuilderChange } from '.';
6-
import { css, cx, injectGlobal } from '@emotion/css';
6+
import { css, cx } from '@emotion/css';
77
import DatePicker from 'react-datepicker';
88
import 'react-datepicker/dist/react-datepicker.css';
99

10-
injectGlobal(`
11-
.react-datepicker__triangle {
12-
display: none;
13-
}
14-
.react-datepicker-popper {
15-
z-index: 1000 !important;
16-
}
17-
`);
1810

1911
interface Props extends QueryBuilderFieldProps {
2012
format: string;
@@ -68,8 +60,7 @@ export const DateInterval = (props: Props) => {
6860
onBuilderChange(props, intervalStart + '/' + value);
6961
};
7062
const { label, description, format, time } = props;
71-
const theme = useTheme();
72-
const styles = getStyles(theme);
63+
const styles = useStyles2(getStyles);
7364
return (
7465
<>
7566
<InlineLabel tooltip={description} width="auto">
@@ -105,14 +96,18 @@ export const DateInterval = (props: Props) => {
10596
);
10697
};
10798

108-
const getStyles = stylesFactory((theme: GrafanaTheme) => {
109-
return {
110-
picker: css`
111-
& input {
112-
border: 1px solid ${theme.colors.border2};
113-
height: 32px;
114-
margin-right: 4px;
115-
}
116-
`,
117-
};
99+
const getStyles = (theme: GrafanaTheme2) => ({
100+
picker: css`
101+
& input {
102+
border: 1px solid ${theme.colors.border.medium};
103+
height: 32px;
104+
margin-right: 4px;
105+
}
106+
.react-datepicker__triangle {
107+
display: none;
108+
}
109+
.react-datepicker-popper {
110+
z-index: 1000 !important;
111+
}
112+
`,
118113
});

src/builder/abstract/DateTime.tsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
import React, { ChangeEvent } from 'react';
2-
import { InlineLabel, stylesFactory, useTheme } from '@grafana/ui';
3-
import { GrafanaTheme } from '@grafana/data';
2+
import { InlineLabel, useStyles2 } from '@grafana/ui';
3+
import { GrafanaTheme2 } from '@grafana/data';
44
import { QueryBuilderFieldProps } from './types';
55
import { onBuilderChange } from '.';
6-
import { css, cx, injectGlobal } from '@emotion/css';
6+
import { css, cx } from '@emotion/css';
77
import DatePicker from 'react-datepicker';
88
import 'react-datepicker/dist/react-datepicker.css';
99

10-
injectGlobal(`
11-
.react-datepicker__triangle {
12-
display: none;
13-
}
14-
.react-datepicker-popper {
15-
z-index: 1000 !important;
16-
}
17-
`);
1810

1911
interface Props extends QueryBuilderFieldProps {
2012
format: string;
@@ -45,8 +37,7 @@ export const DateTime = (props: Props) => {
4537
onBuilderChange(props, date.toISOString());
4638
};
4739
const { label, description, format, time } = props;
48-
const theme = useTheme();
49-
const styles = getStyles(theme);
40+
const styles = useStyles2(getStyles);
5041
return (
5142
<>
5243
<InlineLabel tooltip={description} width="auto">
@@ -65,14 +56,18 @@ export const DateTime = (props: Props) => {
6556
);
6657
};
6758

68-
const getStyles = stylesFactory((theme: GrafanaTheme) => {
69-
return {
70-
picker: css`
71-
& input {
72-
border: 1px solid ${theme.colors.border2};
73-
height: 32px;
74-
margin-right: 4px;
75-
}
76-
`,
77-
};
59+
const getStyles = (theme: GrafanaTheme2) => ({
60+
picker: css`
61+
& input {
62+
border: 1px solid ${theme.colors.border.medium};
63+
height: 32px;
64+
margin-right: 4px;
65+
}
66+
.react-datepicker__triangle {
67+
display: none;
68+
}
69+
.react-datepicker-popper {
70+
z-index: 1000 !important;
71+
}
72+
`,
7873
});

src/builder/abstract/Row.tsx

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
import React, { ReactNode } from 'react';
2-
import { InlineFieldRow, stylesFactory, useTheme } from '@grafana/ui';
3-
import { GrafanaTheme } from '@grafana/data';
2+
import { InlineFieldRow, useStyles2 } from '@grafana/ui';
3+
import { GrafanaTheme2 } from '@grafana/data';
44
import { css, cx } from '@emotion/css';
55

66
interface Props {
77
children: ReactNode | ReactNode[];
88
}
99

1010
export const Row = (props: Props) => {
11-
const theme = useTheme();
12-
const styles = getStyles(theme);
11+
const styles = useStyles2(getStyles);
1312
return <InlineFieldRow className={cx(styles.row)}>{props.children}</InlineFieldRow>;
1413
};
1514

16-
const getStyles = stylesFactory((theme: GrafanaTheme) => {
17-
return {
18-
row: css`
19-
width: 100%;
20-
padding-bottom: 5px;
21-
& > & {
22-
border-left: 1px solid ${theme.colors.border2};
23-
padding: 5px 0 5px 10px;
24-
}
25-
`,
26-
};
15+
const getStyles = (theme: GrafanaTheme2) => ({
16+
row: css`
17+
width: 100%;
18+
padding-bottom: 5px;
19+
& > & {
20+
border-left: 1px solid ${theme.colors.border.medium};
21+
padding: 5px 0 5px 10px;
22+
}
23+
`,
2724
});

src/configuration/QuerySettings/DruidQueryContextSettings.tsx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { ChangeEvent } from 'react';
2-
import { InlineLabel, InlineField, InlineFieldRow, Input, Button, Icon, useTheme, stylesFactory } from '@grafana/ui';
3-
import { GrafanaTheme } from '@grafana/data';
2+
import { InlineLabel, InlineField, InlineFieldRow, Input, Button, Icon, useStyles2 } from '@grafana/ui';
3+
import { GrafanaTheme2 } from '@grafana/data';
44
import { css, cx } from '@emotion/css';
55
import { QuerySettingsProps } from './types';
66

@@ -26,8 +26,7 @@ const useParameters = (props: QuerySettingsProps): any => {
2626
};
2727

2828
export const DruidQueryContextSettings = (props: QuerySettingsProps) => {
29-
const theme = useTheme();
30-
const styles = getStyles(theme);
29+
const styles = useStyles2(getStyles);
3130
const [parameters, setParameters] = useParameters(props);
3231
const onParameterChange = (name: string, parameter: Parameter) => {
3332
setParameters({ ...parameters, [name]: parameter });
@@ -75,16 +74,14 @@ export const DruidQueryContextSettings = (props: QuerySettingsProps) => {
7574
);
7675
};
7776

78-
const getStyles = stylesFactory((theme: GrafanaTheme) => {
79-
return {
80-
row: css`
81-
width: 100%;
82-
& > & {
83-
border-left: 1px solid ${theme.colors.border2};
84-
padding: 5px 0px 0px 10px;
85-
}
86-
`,
87-
};
77+
const getStyles = (theme: GrafanaTheme2) => ({
78+
row: css`
79+
width: 100%;
80+
& > & {
81+
border-left: 1px solid ${theme.colors.border.medium};
82+
padding: 5px 0px 0px 10px;
83+
}
84+
`,
8885
});
8986

9087
interface Parameter {

0 commit comments

Comments
 (0)