Skip to content

Commit c5b9345

Browse files
committed
Refactor variable query
1 parent 36558a0 commit c5b9345

File tree

7 files changed

+104
-151
lines changed

7 files changed

+104
-151
lines changed

src/components/QueryEditor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import {
1212
import { EditorHeader, InlineSelect, FlexItem } from '@grafana/plugin-ui';
1313
import { CoreApp, QueryEditorProps, SelectableValue, LoadingState } from '@grafana/data';
1414
import { MongoDBDataSource } from '../datasource';
15-
import { MongoDataSourceOptions, MongoQuery, QueryLanguage, QueryType, DEFAULT_QUERY } from '../types';
15+
import { MongoDataSourceOptions, MongoDBQuery, QueryLanguage, QueryType, DEFAULT_QUERY } from '../types';
1616
import { parseJsQuery, parseJsQueryLegacy } from '../utils';
1717
import { QueryEditorRaw } from './QueryEditorRaw';
1818
import { QueryToolbox } from './QueryToolbox';
1919
import validator from 'validator';
2020

21-
type Props = QueryEditorProps<MongoDBDataSource, MongoQuery, MongoDataSourceOptions>;
21+
type Props = QueryEditorProps<MongoDBDataSource, MongoDBQuery, MongoDataSourceOptions>;
2222

2323
const queryTypes: Array<SelectableValue<string>> = [
2424
{
Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,66 @@
1-
import React, { useState } from 'react';
2-
import { VariableQuery } from '../types';
3-
import { InlineField, Input, Alert, InlineFieldRow, Button } from '@grafana/ui';
1+
import React, { useEffect, useState } from 'react';
2+
import { InlineField, Input, Alert, Button } from '@grafana/ui';
3+
4+
import { EditorRow, EditorRows } from '@grafana/plugin-ui';
5+
import { QueryEditorProps } from '@grafana/data';
6+
import { MongoDataSourceOptions, MongoDBQuery, MongoDBVariableQuery, QueryType } from '../types';
47
import { QueryEditorRaw } from './QueryEditorRaw';
8+
import { MongoDBDataSource } from 'datasource';
9+
10+
const refId = 'MongoDBVariableQueryEditor-VariableQuery';
11+
12+
export type VariableQueryEditorProps = QueryEditorProps<
13+
MongoDBDataSource,
14+
MongoDBQuery,
15+
MongoDataSourceOptions,
16+
MongoDBVariableQuery
17+
>;
518

6-
interface VariableQueryProps {
7-
query: VariableQuery;
8-
onChange: (query: VariableQuery, definition: string) => void;
9-
}
19+
export const VariableQueryEditor = ({ onChange, query }: VariableQueryEditorProps) => {
20+
const [collection, setCollection] = useState('');
21+
const [queryText, setQueryText] = useState('');
22+
23+
useEffect(() => {
24+
setCollection(query.collection ?? '');
25+
setQueryText(query.queryText ?? '');
26+
}, [query]);
1027

11-
export const VariableQueryEditor = ({ onChange, query }: VariableQueryProps) => {
12-
const [state, setState] = useState(query);
1328
return (
14-
<>
15-
<InlineFieldRow>
29+
<EditorRows>
30+
<EditorRow>
1631
<InlineField
1732
label="Collection"
1833
tooltip="Name of MongoDB collection to query"
1934
error="Collection is required"
20-
invalid={!state.collection}
35+
invalid={!collection}
2136
>
2237
<Input
2338
name="collection"
24-
onChange={(evt) => setState({ ...state, collection: evt.currentTarget.value })}
25-
value={state.collection}
39+
onChange={(evt) => {
40+
setCollection(evt.currentTarget.value);
41+
}}
42+
value={collection}
2643
></Input>
2744
</InlineField>
2845
<Button
2946
onClick={() => {
30-
onChange(
31-
{ queryText: state.queryText, collection: state.collection },
32-
`${state.collection} (${state.queryText})`,
33-
);
47+
onChange({ queryText: queryText, queryType: QueryType.TABLE, refId: refId, collection: collection });
3448
}}
3549
>
3650
Save and Query
3751
</Button>
38-
</InlineFieldRow>
39-
52+
</EditorRow>
4053
<QueryEditorRaw
41-
query={query.queryText ?? ''}
54+
query={queryText}
4255
language="json"
43-
onBlur={(queryText) => setState({ ...query, queryText: queryText })}
56+
onBlur={(queryText) => setQueryText(queryText)}
4457
height={300}
4558
fontSize={14}
4659
/>
4760
<Alert title="Query info" severity="info" style={{ marginTop: 2 }}>
4861
The query result is expected to contain <code>value</code> field which has elements of type <code>string</code>{' '}
4962
or <code>number</code>
5063
</Alert>
51-
</>
64+
</EditorRows>
5265
);
5366
};

src/datasource.ts

Lines changed: 22 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,34 @@
1-
import {
2-
DataSourceInstanceSettings,
3-
CoreApp,
4-
ScopedVars,
5-
DataQueryRequest,
6-
LegacyMetricFindQueryOptions,
7-
MetricFindValue,
8-
dateTime,
9-
LiveChannelScope,
10-
DataQueryResponse,
11-
LoadingState
12-
} from '@grafana/data';
1+
import { DataSourceInstanceSettings, CoreApp, ScopedVars, DataQueryRequest, LiveChannelScope, DataQueryResponse, LoadingState, rangeUtil } from '@grafana/data';
132
import { DataSourceWithBackend, getGrafanaLiveSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
14-
import {
15-
parseJsQuery,
16-
getBucketCount,
17-
parseJsQueryLegacy,
18-
randomId,
19-
getMetricValues,
20-
datetimeToJson,
21-
base64UrlEncode,
22-
unixTsToMongoID
23-
} from './utils';
24-
import { MongoQuery, MongoDataSourceOptions, DEFAULT_QUERY, QueryLanguage, VariableQuery } from './types';
25-
import { firstValueFrom, merge, Observable, of } from 'rxjs';
3+
import { parseJsQuery, getBucketCount, parseJsQueryLegacy, datetimeToJson, base64UrlEncode, unixTsToMongoID } from './utils';
4+
import { MongoDBQuery, MongoDataSourceOptions, DEFAULT_QUERY, QueryLanguage } from './types';
5+
import { merge, Observable, of } from 'rxjs';
266
import { MongoDBVariableSupport } from 'variables'
277

28-
export class MongoDBDataSource extends DataSourceWithBackend<MongoQuery, MongoDataSourceOptions> {
8+
export class MongoDBDataSource extends DataSourceWithBackend<MongoDBQuery, MongoDataSourceOptions> {
299
constructor(instanceSettings: DataSourceInstanceSettings<MongoDataSourceOptions>,
3010
private readonly templateSrv: TemplateSrv = getTemplateSrv()) {
3111
super(instanceSettings);
32-
this.variables = new MongoDBVariableSupport(this, this.templateSrv);
12+
this.variables = new MongoDBVariableSupport(this);
3313
}
3414

35-
getDefaultQuery(_: CoreApp): Partial<MongoQuery> {
15+
getDefaultQuery(_: CoreApp): Partial<MongoDBQuery> {
3616
return DEFAULT_QUERY;
3717
}
3818

39-
applyTemplateVariables(query: MongoQuery, scopedVars: ScopedVars) {
40-
let queryText = query.queryText!;
19+
applyTemplateVariables(query: MongoDBQuery, scopedVars: ScopedVars) {
20+
console.log("scopedVars", scopedVars);
21+
console.log("Vars", this.templateSrv.getVariables());
22+
const variables = { ...scopedVars };
23+
24+
25+
const from = this.templateSrv.replace('$__from', variables);
26+
const to = this.templateSrv.replace('$__to', variables);
4127

28+
variables.from = { value: datetimeToJson(from) }
29+
variables.to = { value: datetimeToJson(to) }
30+
31+
let queryText = query.queryText!;
4232
if (query.queryLanguage === QueryLanguage.JAVASCRIPT || query.queryLanguage === QueryLanguage.JAVASCRIPT_SHADOW) {
4333
const { jsonQuery } =
4434
query.queryLanguage === QueryLanguage.JAVASCRIPT_SHADOW
@@ -47,31 +37,19 @@ export class MongoDBDataSource extends DataSourceWithBackend<MongoQuery, MongoDa
4737
queryText = jsonQuery!;
4838
}
4939

50-
const from = getTemplateSrv().replace('$__from', {});
51-
const to = getTemplateSrv().replace('$__to', {});
5240

5341
queryText = queryText
5442
.replaceAll(/"\$__from_oid"/g, `"${unixTsToMongoID(from, '0')}"`)
5543
.replaceAll(/"\$__to_oid"/g, `"${unixTsToMongoID(to, 'f')}"`);
5644

57-
// Compatible with legacy plugin $from
58-
if (from !== '$__from') {
59-
queryText = queryText.replaceAll(/"\$from"/g, datetimeToJson(from));
60-
}
61-
62-
// Compatible with legacy plugin $to
63-
if (to !== '$__to') {
64-
queryText = queryText.replaceAll(/"\$to"/g, datetimeToJson(to));
65-
}
66-
6745
const interval = scopedVars['__interval_ms']?.value;
6846

6947
// Compatible with legacy plugin $dateBucketCount
7048
if (interval && from && to) {
7149
queryText = queryText.replaceAll(/"\$dateBucketCount"/g, getBucketCount(from, to, interval).toString());
7250
}
7351

74-
const text = getTemplateSrv().replace(queryText, scopedVars);
52+
const text = this.templateSrv.replace(queryText, variables);
7553
return {
7654
...query,
7755
queryText: text,
@@ -80,48 +58,11 @@ export class MongoDBDataSource extends DataSourceWithBackend<MongoQuery, MongoDa
8058

8159
annotations = {}
8260

83-
async metricFindQuery(query: VariableQuery, options?: LegacyMetricFindQueryOptions): Promise<MetricFindValue[]> {
84-
const request: DataQueryRequest<MongoQuery> = {
85-
requestId: 'variable-query-' + randomId(3),
86-
targets: [
87-
{
88-
refId: 'A',
89-
queryLanguage: QueryLanguage.JSON,
90-
collection: query.collection,
91-
queryText: getTemplateSrv().replace(query.queryText),
92-
queryType: 'table',
93-
isStreaming: false,
94-
},
95-
],
96-
scopedVars: options?.scopedVars || {},
97-
interval: '5s',
98-
timezone: 'browser',
99-
intervalMs: 5000,
100-
range: options?.range || {
101-
from: dateTime(),
102-
to: dateTime(),
103-
raw: {
104-
from: 'now',
105-
to: 'now',
106-
},
107-
},
108-
app: 'variable-query',
109-
startTime: (options?.range?.from || dateTime()).toDate().getUTCMilliseconds(),
110-
};
111-
112-
const resp = await firstValueFrom(this.query(request));
113-
if (resp.errors?.length && resp.errors.length > 0) {
114-
throw new Error(resp.errors[0].message || 'Unknown error');
115-
}
116-
117-
return getMetricValues(resp);
118-
}
119-
120-
filterQuery(query: MongoQuery): boolean {
61+
filterQuery(query: MongoDBQuery): boolean {
12162
return !!query.queryText && !!query.collection;
12263
}
12364

124-
query(request: DataQueryRequest<MongoQuery>): Observable<DataQueryResponse> {
65+
query(request: DataQueryRequest<MongoDBQuery>): Observable<DataQueryResponse> {
12566
if (request.liveStreaming) {
12667
const observables = request.targets.map((query) => {
12768
return getGrafanaLiveSrv().getDataStream({

src/module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { MongoDBDataSource } from './datasource';
33
import { ConfigEditor } from './components/ConfigEditor';
44
import { QueryEditor } from './components/QueryEditor';
55
import { QueryHelper } from './components/QueryHelper';
6-
import { MongoQuery, MongoDataSourceOptions } from './types';
6+
import { MongoDBQuery, MongoDataSourceOptions } from './types';
77

8-
export const plugin = new DataSourcePlugin<MongoDBDataSource, MongoQuery, MongoDataSourceOptions>(MongoDBDataSource)
8+
export const plugin = new DataSourcePlugin<MongoDBDataSource, MongoDBQuery, MongoDataSourceOptions>(MongoDBDataSource)
99
.setConfigEditor(ConfigEditor)
1010
.setQueryEditor(QueryEditor)
1111
.setQueryEditorHelp(QueryHelper)

src/types.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DataSourceJsonData } from '@grafana/data';
22
import { DataQuery } from '@grafana/schema';
33

4-
export interface MongoQuery extends DataQuery {
4+
export interface MongoDBQuery extends DataQuery {
55
queryText?: string;
66
collection?: string;
77
queryType?: string;
@@ -18,6 +18,11 @@ export interface MongoQuery extends DataQuery {
1818
aggregateBypassDocumentValidation?: boolean;
1919
}
2020

21+
export interface MongoDBVariableQuery extends DataQuery {
22+
queryText?: string;
23+
collection?: string;
24+
}
25+
2126
export const QueryType = {
2227
TIMESERIES: 'timeseries',
2328
TABLE: 'table',
@@ -29,7 +34,7 @@ export const QueryLanguage = {
2934
JAVASCRIPT_SHADOW: 'javascriptShadow',
3035
};
3136

32-
export const DEFAULT_QUERY: Partial<MongoQuery> = {
37+
export const DEFAULT_QUERY: Partial<MongoDBQuery> = {
3338
queryText: '',
3439
queryType: QueryType.TABLE,
3540
queryLanguage: QueryLanguage.JSON,
@@ -52,14 +57,7 @@ export const ConnectionStringScheme = {
5257
DNS_SEED_LIST: 'dns_seed_list',
5358
};
5459

55-
export interface VariableQuery {
56-
collection?: string;
57-
queryText?: string;
58-
}
5960

60-
/**
61-
* These are options configured for each DataSource instance
62-
*/
6361
export interface MongoDataSourceOptions extends DataSourceJsonData {
6462
connectionStringScheme?: string;
6563
host?: string;
@@ -69,9 +67,6 @@ export interface MongoDataSourceOptions extends DataSourceJsonData {
6967
connectionParameters?: string;
7068
}
7169

72-
/**
73-
* Value that is used in the backend, but never sent over HTTP to the frontend
74-
*/
7570
export interface MySecureJsonData {
7671
password?: string;
7772
}

src/utils.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { DataFrameSchema, DataQueryResponse, FieldType, MetricFindValue } from '@grafana/data';
21
import { JsQueryResult } from 'types';
32
import shadow from 'shadowrealm-api';
43
import { getTemplateSrv } from '@grafana/runtime';
@@ -56,7 +55,7 @@ export function datetimeToJson(datetime: string) {
5655
$date: {
5756
$numberLong: datetime,
5857
},
59-
});
58+
})
6059
}
6160

6261
export function getBucketCount(from: string, to: string, intervalMs: number) {
@@ -83,28 +82,6 @@ export function randomId(length: number) {
8382
return result;
8483
}
8584

86-
export function getMetricValues(response: DataQueryResponse): MetricFindValue[] {
87-
const dataframe = response.data[0] as DataFrameSchema;
88-
const field = dataframe.fields.find((f) => f.name === 'value');
89-
90-
if (!field) {
91-
throw new Error('Field "value" not found');
92-
}
93-
94-
if (field.type !== FieldType.string && field.type !== FieldType.number) {
95-
throw new Error('Each element should be string or number');
96-
}
97-
98-
// @ts-ignore
99-
return field.values.map((value: string | number) => {
100-
return {
101-
text: value.toString(),
102-
value: value,
103-
expandable: true,
104-
};
105-
});
106-
}
107-
10885
export function base64UrlEncode(input: string | undefined) {
10986
if (!input) {
11087
return '';

0 commit comments

Comments
 (0)