Skip to content

Commit 58d3516

Browse files
committed
Simple text input groupby
1 parent 0f2e47d commit 58d3516

File tree

5 files changed

+53
-33
lines changed

5 files changed

+53
-33
lines changed

cmd/fire/fire.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ scrape_configs:
66
scrape_interval: "2s"
77
static_configs:
88
- targets: [ '127.0.0.1:4100']
9+
10+
- job_name: "foo"
11+
scrape_interval: "5s"
12+
static_configs:
13+
- targets: [ '127.0.0.1:4100']

grafana/fire-datasource/pkg/plugin/query.go

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type queryModel struct {
2121
WithStreaming bool
2222
ProfileTypeID string `json:"profileTypeId"`
2323
LabelSelector string `json:"labelSelector"`
24+
GroupBy string `json:"groupBy"`
2425
}
2526

2627
type dsJsonModel struct {
@@ -68,8 +69,7 @@ func (d *FireDatasource) query(ctx context.Context, pCtx backend.PluginContext,
6869
Start: query.TimeRange.From.UnixMilli(),
6970
End: query.TimeRange.To.UnixMilli(),
7071
Step: math.Max(query.Interval.Seconds(), parsedInterval.Seconds()),
71-
// todo add one or more group bys
72-
GroupBy: []string{},
72+
GroupBy: strings.Split(qm.GroupBy, ","),
7373
})
7474

7575
log.DefaultLogger.Debug("Sending SelectSeriesRequest", "request", req, "queryModel", qm)
@@ -80,7 +80,7 @@ func (d *FireDatasource) query(ctx context.Context, pCtx backend.PluginContext,
8080
return response
8181
}
8282
// add the frames to the response.
83-
response.Frames = append(response.Frames, seriesToDataFrame(seriesResp, qm.ProfileTypeID))
83+
response.Frames = append(response.Frames, seriesToDataFrames(seriesResp, qm.ProfileTypeID)...)
8484
}
8585

8686
if query.QueryType == queryTypeProfile || query.QueryType == queryTypeBoth {
@@ -285,41 +285,44 @@ func walkTree(tree *ProfileTree, fn func(tree *ProfileTree)) {
285285
}
286286
}
287287

288-
func seriesToDataFrame(seriesResp *connect.Response[querierv1.SelectSeriesResponse], profileTypeID string) *data.Frame {
289-
frame := data.NewFrame("series")
290-
frame.Meta = &data.FrameMeta{PreferredVisualization: "graph"}
288+
func seriesToDataFrames(seriesResp *connect.Response[querierv1.SelectSeriesResponse], profileTypeID string) []*data.Frame {
289+
var frames []*data.Frame
291290

292-
fields := data.Fields{}
293-
timeField := data.NewField("time", nil, []time.Time{})
294-
fields = append(fields, timeField)
291+
for _, series := range seriesResp.Msg.Series {
292+
// We create separate data frames as the series may not have the same length
293+
frame := data.NewFrame("series")
294+
frame.Meta = &data.FrameMeta{PreferredVisualization: "graph"}
295+
296+
fields := data.Fields{}
297+
timeField := data.NewField("time", nil, []time.Time{})
298+
fields = append(fields, timeField)
295299

296-
for index, series := range seriesResp.Msg.Series {
297300
label := ""
298301
unit := ""
299-
if len(series.Labels) > 0 {
300-
label = series.Labels[0].Name
301-
} else {
302-
parts := strings.Split(profileTypeID, ":")
303-
if len(parts) == 5 {
304-
label = parts[1] // sample type e.g. cpu, goroutine, alloc_objects
305-
unit = normalizeUnit(parts[2])
306-
}
302+
parts := strings.Split(profileTypeID, ":")
303+
if len(parts) == 5 {
304+
label = parts[1] // sample type e.g. cpu, goroutine, alloc_objects
305+
unit = normalizeUnit(parts[2])
306+
}
307+
308+
labels := make(map[string]string)
309+
for _, label := range series.Labels {
310+
labels[label.Name] = label.Value
307311
}
308-
valueField := data.NewField(label, nil, []float64{})
312+
313+
valueField := data.NewField(label, labels, []float64{})
309314
valueField.Config = &data.FieldConfig{Unit: unit}
310315

311316
for _, point := range series.Points {
312-
if index == 0 {
313-
timeField.Append(time.UnixMilli(point.Timestamp))
314-
}
317+
timeField.Append(time.UnixMilli(point.Timestamp))
315318
valueField.Append(point.Value)
316319
}
317320

318321
fields = append(fields, valueField)
322+
frame.Fields = fields
323+
frames = append(frames, frame)
319324
}
320-
321-
frame.Fields = fields
322-
return frame
325+
return frames
323326
}
324327

325328
func normalizeUnit(unit string) string {

grafana/fire-datasource/src/QueryEditor/QueryEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export function QueryEditor(props: Props) {
8484
<EditorRow>
8585
<QueryOptions
8686
query={query}
87-
onQueryTypeChange={(val) => props.onChange({ ...query, queryType: val as Query['queryType'] })}
87+
onQueryChange={props.onChange}
8888
app={props.app}
8989
/>
9090
</EditorRow>

grafana/fire-datasource/src/QueryEditor/QueryOptions.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { css } from '@emotion/css';
22
import React from 'react';
33
import { useToggle } from 'react-use';
44

5-
import {CoreApp, GrafanaTheme2} from '@grafana/data';
6-
import { Icon, useStyles2, RadioButtonGroup, Field } from '@grafana/ui';
5+
import { CoreApp, GrafanaTheme2 } from '@grafana/data';
6+
import { Icon, useStyles2, RadioButtonGroup, Field, Input } from '@grafana/ui';
77
import { Query } from '../types';
88
import { Stack } from './Stack';
99

1010
export interface Props {
1111
query: Query;
12-
onQueryTypeChange: (val: string) => void;
12+
onQueryChange: (query: Query) => void;
1313
app?: CoreApp;
1414
}
1515

@@ -20,7 +20,7 @@ const rangeOptions: Array<{ value: Query['queryType']; label: string; descriptio
2020
];
2121

2222
function getOptions(app?: CoreApp) {
23-
if (app === CoreApp.Explore) {
23+
if (app === CoreApp.Explore) {
2424
return rangeOptions;
2525
}
2626
return rangeOptions.filter((option) => option.value !== 'both');
@@ -29,10 +29,10 @@ if (app === CoreApp.Explore) {
2929
/**
3030
* Base on QueryOptionGroup component from grafana/ui but that is not available yet.
3131
*/
32-
export function QueryOptions({ query, onQueryTypeChange, app }: Props) {
32+
export function QueryOptions({ query, onQueryChange, app }: Props) {
3333
const [isOpen, toggleOpen] = useToggle(false);
3434
const styles = useStyles2(getStyles);
35-
const options = getOptions(app)
35+
const options = getOptions(app);
3636

3737
return (
3838
<Stack gap={0} direction="column">
@@ -50,7 +50,17 @@ export function QueryOptions({ query, onQueryTypeChange, app }: Props) {
5050
{isOpen && (
5151
<div className={styles.body}>
5252
<Field label={'Query Type'}>
53-
<RadioButtonGroup options={options} value={query.queryType} onChange={onQueryTypeChange} />
53+
<RadioButtonGroup
54+
options={options}
55+
value={query.queryType}
56+
onChange={(value) => onQueryChange({ ...query, queryType: value })}
57+
/>
58+
</Field>
59+
<Field label={'Group by'}>
60+
<Input
61+
value={query.groupBy}
62+
onChange={(ev) => onQueryChange({ ...query, groupBy: ev.currentTarget.value })}
63+
/>
5464
</Field>
5565
</div>
5666
)}

grafana/fire-datasource/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface Query extends DataQuery {
44
labelSelector: string;
55
profileTypeId: string;
66
queryType: 'metrics' | 'profile' | 'both';
7+
groupBy: string;
78
}
89

910
export interface ProfileTypeMessage {
@@ -20,6 +21,7 @@ export type SeriesMessage = Array<{ labels: Array<{ name: string; value: string
2021
export const defaultQuery: Partial<Query> = {
2122
labelSelector: '{}',
2223
queryType: 'both',
24+
groupBy: '',
2325
};
2426

2527
/**

0 commit comments

Comments
 (0)