Skip to content

Commit 71acc83

Browse files
authored
Add Streaming capabilities to visualize INFO command (#57)
* Add Streaming query * Update Query Editor and add Streaming dashboard * Update README and add screenshot
1 parent d0476c2 commit 71acc83

File tree

8 files changed

+1270
-4
lines changed

8 files changed

+1270
-4
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
[![Redis Enterprise](https://img.shields.io/badge/Redis%20Enterprise-supported-orange)](https://redislabs.com/redis-enterprise/)
99
[![Go Report Card](https://goreportcard.com/badge/github.com/RedisTimeSeries/grafana-redis-datasource)](https://goreportcard.com/report/github.com/RedisTimeSeries/grafana-redis-datasource)
1010
[![CircleCI](https://circleci.com/gh/RedisTimeSeries/grafana-redis-datasource.svg?style=svg)](https://circleci.com/gh/RedisTimeSeries/grafana-redis-datasource)
11+
[![Downloads](https://img.shields.io/badge/dynamic/json?color=green&label=downloads&query=%24.downloads&url=https%3A%2F%2Fgrafana.com%2Fapi%2Fplugins%2Fredis-datasource)](https://grafana.com/grafana/plugins/redis-datasource)
1112

1213
## Summary
1314

@@ -73,7 +74,7 @@ Project provides `docker-compose.yml` to start Redis with RedisTimeSeries module
7374
docker-compose up
7475
```
7576

76-
Open Grafana in your browser [http://localhost:3000](http://localhost:3000) and configure Redis Data Source. You can add as many data sources as you want to support multiple Redis databases.
77+
Open Grafana in your browser and configure Redis Data Source. You can add as many data sources as you want to support multiple Redis databases.
7778

7879
![Datasource](https://raw.githubusercontent.com/RedisTimeSeries/grafana-redis-datasource/master/src/img/datasource.png)
7980

@@ -146,4 +147,4 @@ We love to hear from users, developers and the whole community interested by thi
146147

147148
## License
148149

149-
- Apache License Version 2.0, see [LICENSE](LICENSE)
150+
- Apache License Version 2.0, see [LICENSE](https://github.com/RedisTimeSeries/grafana-redis-datasource/blob/master/LICENSE)

src/DataSource.ts

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
import { head } from 'lodash';
2+
import { Observable } from 'rxjs';
13
import { map as map$, switchMap as switchMap$ } from 'rxjs/operators';
2-
import { DataFrame, DataQueryRequest, DataSourceInstanceSettings, MetricFindValue, ScopedVars } from '@grafana/data';
4+
import {
5+
CircularDataFrame,
6+
DataFrame,
7+
DataQueryRequest,
8+
DataQueryResponse,
9+
DataSourceInstanceSettings,
10+
FieldType,
11+
MetricFindValue,
12+
ScopedVars,
13+
} from '@grafana/data';
314
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
415
import { RedisQuery } from './redis';
516
import { RedisDataSourceOptions } from './types';
@@ -69,4 +80,82 @@ export class DataSource extends DataSourceWithBackend<RedisQuery, RedisDataSourc
6980
filter: query.filter ? templateSrv.replace(query.filter, scopedVars) : '',
7081
};
7182
}
83+
84+
/**
85+
* Override query to support streaming
86+
*/
87+
query(request: DataQueryRequest<RedisQuery>): Observable<DataQueryResponse> {
88+
const refA = head(request.targets);
89+
90+
/**
91+
* No streaming enabled
92+
*/
93+
if (!refA?.streaming) {
94+
return super.query(request);
95+
}
96+
97+
/**
98+
* Streaming enabled
99+
*/
100+
return new Observable<DataQueryResponse>((subscriber) => {
101+
/**
102+
* This dataframe can have values constantly added, and will never exceed the given capacity
103+
*/
104+
const frame = new CircularDataFrame({
105+
append: 'tail',
106+
capacity: refA?.streamingCapacity || 1000,
107+
});
108+
109+
/**
110+
* Set refId and Time field
111+
*/
112+
frame.refId = refA.refId;
113+
frame.addField({ name: 'time', type: FieldType.time });
114+
115+
/**
116+
* Interval
117+
*/
118+
const intervalId = setInterval(async () => {
119+
let values: { [index: string]: number } = { time: Date.now() };
120+
121+
/**
122+
* Run Query
123+
*/
124+
const fields = await super
125+
.query(request)
126+
.pipe(
127+
switchMap$((response) => response.data),
128+
map$((data: DataFrame) => data.fields)
129+
)
130+
.toPromise();
131+
132+
if (fields) {
133+
/**
134+
* Add fields to frame fields and return values
135+
*/
136+
fields.map((field) =>
137+
field.values.toArray().map((value) => {
138+
if (frame.fields.length < fields.length + 1) {
139+
frame.addField({ name: field.name, type: field.type });
140+
}
141+
return (values[field.name] = value);
142+
})
143+
);
144+
}
145+
146+
/**
147+
* Add frame
148+
*/
149+
frame.add(values);
150+
subscriber.next({
151+
data: [frame],
152+
key: refA.refId,
153+
});
154+
}, refA.streamingInterval || 1000);
155+
156+
return () => {
157+
clearInterval(intervalId);
158+
};
159+
});
160+
}
72161
}

src/components/QueryEditor.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ export class QueryEditor extends PureComponent<Props> {
160160
type,
161161
section,
162162
fill,
163+
streaming,
164+
streamingInterval,
165+
streamingCapacity,
166+
refId,
163167
} = this.props.query;
164168
const { onRunQuery, onChange } = this.props;
165169

@@ -314,6 +318,44 @@ export class QueryEditor extends PureComponent<Props> {
314318
</div>
315319
)}
316320

321+
{refId === 'A' && (
322+
<div className="gf-form">
323+
<Switch
324+
label="Streaming"
325+
labelClass="width-8"
326+
tooltip="If checked, the datasource will stream data. Only Ref A is supported. Command should return only one line of data."
327+
checked={streaming || false}
328+
onChange={(event) => {
329+
onChange({ ...this.props.query, streaming: event.currentTarget.checked });
330+
}}
331+
/>
332+
{streaming && (
333+
<>
334+
<FormField
335+
labelWidth={8}
336+
value={streamingInterval}
337+
type="number"
338+
onChange={(event: ChangeEvent<HTMLInputElement>) => {
339+
onChange({ ...this.props.query, streamingInterval: Number(event.target.value) });
340+
}}
341+
label="Interval"
342+
tooltip="Streaming interval in milliseconds. Default is 1000ms."
343+
/>
344+
<FormField
345+
labelWidth={8}
346+
value={streamingCapacity}
347+
type="number"
348+
onChange={(event: ChangeEvent<HTMLInputElement>) => {
349+
onChange({ ...this.props.query, streamingCapacity: Number(event.target.value) });
350+
}}
351+
label="Capacity"
352+
tooltip="Values will be constantly added and will never exceed the given capacity. Default is 1000."
353+
/>
354+
</>
355+
)}
356+
</div>
357+
)}
358+
317359
<Button onClick={onRunQuery}>Run</Button>
318360
</div>
319361
);

0 commit comments

Comments
 (0)