Skip to content

Commit c603652

Browse files
authored
added support to select organization (#2)
1 parent 87a35b2 commit c603652

File tree

7 files changed

+196
-135
lines changed

7 files changed

+196
-135
lines changed

src/components/QueryEditor.tsx

Lines changed: 119 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,79 @@ import { InlineLabel, QueryField, Select, Switch } from '@grafana/ui';
33
import { QueryEditorProps } from '@grafana/data';
44
import { DataSource } from '../datasource';
55
import { MyDataSourceOptions, MyQuery } from '../types';
6-
import { getStreams } from '../streams';
6+
import { getStreams } from '../services/streams';
7+
import { getOrganizations } from '../services/organizations';
78
import { css } from '@emotion/css';
89

910
type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;
1011

1112
export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props) {
1213
const [streams, setStreams]: any = useState({});
1314
const [streamOptions, setStreamOptions]: any = useState([]);
15+
const [orgOptions, setOrgOptions]: any = useState([]);
1416

1517
useEffect(() => {
16-
getStreams(datasource.url).then((response: any) => {
17-
const streams: { [key: string]: any } = {};
18-
response.list.forEach((stream: any) => {
19-
streams[stream.name] = stream;
20-
});
21-
setStreams({ ...streams });
22-
onChange({
23-
...query,
24-
query: query.query || '',
25-
stream: response.list[0].name,
26-
streamFields: response.list[0].schema,
27-
sqlMode: false,
28-
});
29-
setStreamOptions([
30-
...response.list.map((stream: any) => ({
31-
label: stream.name,
32-
value: stream.name,
33-
})),
34-
]);
35-
onRunQuery();
36-
});
18+
getOrganizations({ url: datasource.url, page_num: 0, page_size: 1000, sort_by: 'id' })
19+
.then((orgs: any) => {
20+
setOrgOptions([
21+
...orgs.data.map((org: any) => ({
22+
label: org.name,
23+
value: org.name,
24+
})),
25+
]);
26+
setupStreams(orgs.data[0].name).then((streams: any) => {
27+
onChange({
28+
...query,
29+
query: '',
30+
stream: streams[0].name,
31+
organization: orgs.data[0].name,
32+
streamFields: streams[0].schema,
33+
sqlMode: false,
34+
});
35+
setStreamOptions([
36+
...Object.values(streams).map((stream: any) => ({
37+
label: stream.name,
38+
value: stream.name,
39+
})),
40+
]);
41+
onRunQuery();
42+
});
43+
})
44+
.catch((err) => console.log(err));
3745
// eslint-disable-next-line react-hooks/exhaustive-deps
3846
}, []);
3947

4048
useEffect(() => {
41-
if (query.sqlMode !== undefined) {
49+
if (query.sqlMode !== undefined && query.stream) {
4250
updateQuery();
4351
}
4452

4553
// eslint-disable-next-line react-hooks/exhaustive-deps
46-
}, [query.sqlMode]);
54+
}, [query.sqlMode, query.organization, query.stream]);
55+
56+
useEffect(() => {
57+
if (query.stream && query.organization) {
58+
updateQuery();
59+
onRunQuery();
60+
}
61+
62+
// eslint-disable-next-line react-hooks/exhaustive-deps
63+
}, [query.organization, query.stream]);
64+
65+
const setupStreams = (orgName: string) => {
66+
return new Promise((resolve) => {
67+
getStreams(datasource.url, orgName)
68+
.then((response: any) => {
69+
const streams: { [key: string]: any } = {};
70+
response.list.forEach((stream: any) => {
71+
streams[stream.name] = stream;
72+
});
73+
setStreams({ ...streams });
74+
resolve(response.list);
75+
})
76+
.catch((err) => console.log(err));
77+
});
78+
};
4779

4880
const updateQuery = () => {
4981
let newQuery = query.query;
@@ -69,12 +101,31 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
69101
const streamUpdated = (stream: any) => {
70102
onChange({
71103
...query,
104+
query: '',
72105
stream: stream.value,
73106
streamFields: streams[stream.value].schema,
74107
});
75108
onRunQuery();
76109
};
77110

111+
const orgUpdated = (organization: any) => {
112+
setupStreams(organization.value).then((streams: any) => {
113+
onChange({
114+
...query,
115+
query: '',
116+
stream: streams[0].name,
117+
organization: organization.value,
118+
streamFields: streams[0].schema,
119+
});
120+
setStreamOptions([
121+
...streams.map((stream: any) => ({
122+
label: stream.name,
123+
value: stream.name,
124+
})),
125+
]);
126+
});
127+
};
128+
78129
const toggleSqlMode = () => {
79130
onChange({
80131
...query,
@@ -106,23 +157,55 @@ export function QueryEditor({ query, onChange, onRunQuery, datasource }: Props)
106157
align-items: center;
107158
`}
108159
>
109-
<InlineLabel
160+
<div
110161
className={css`
111-
width: fit-content;
162+
display: flex;
163+
align-items: center;
164+
padding-right: 1rem;
112165
`}
113-
transparent
114166
>
115-
Select Stream
116-
</InlineLabel>
117-
<Select
167+
<InlineLabel
168+
className={css`
169+
width: fit-content;
170+
`}
171+
transparent
172+
>
173+
Select Stream
174+
</InlineLabel>
175+
<Select
176+
className={css`
177+
width: 200px !important;
178+
margin: 8px 0px;
179+
`}
180+
options={streamOptions}
181+
value={query.stream}
182+
onChange={streamUpdated}
183+
/>
184+
</div>
185+
<div
118186
className={css`
119-
width: 200px !important;
120-
margin: 8px 0px;
187+
display: flex;
188+
align-items: center;
121189
`}
122-
options={streamOptions}
123-
value={query.stream}
124-
onChange={streamUpdated}
125-
/>
190+
>
191+
<InlineLabel
192+
className={css`
193+
width: fit-content;
194+
`}
195+
transparent
196+
>
197+
Select Organization
198+
</InlineLabel>
199+
<Select
200+
className={css`
201+
width: 200px !important;
202+
margin: 8px 0px;
203+
`}
204+
options={orgOptions}
205+
value={query.organization}
206+
onChange={orgUpdated}
207+
/>
208+
</div>
126209
</div>
127210
<QueryField
128211
query={query.query}
Lines changed: 2 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react';
1+
import React from 'react';
22
import { DataSourceHttpSettings } from '@grafana/ui';
33
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
44
import { MyDataSourceOptions } from '../../types';
@@ -8,91 +8,14 @@ interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions>
88
export function ConfigEditor(props: Props) {
99
const { onOptionsChange, options } = props;
1010

11-
useEffect(() => {
12-
console.log(options);
13-
// onOptionsChange({
14-
// ...options,
15-
// jsonData: {
16-
// ...options,
17-
// },
18-
// });
19-
20-
// We can't enforce the eslint rule here because we only want to run this once.
21-
// eslint-disable-next-line react-hooks/exhaustive-deps
22-
}, [options]);
23-
// const onPathChange = (event: ChangeEvent<HTMLInputElement>) => {
24-
// console.log(event, options);
25-
// const jsonData = {
26-
// ...options.jsonData,
27-
// path: event.target.value,
28-
// };
29-
// onOptionsChange({ ...options, jsonData });
30-
// };
31-
32-
// // Secure field (only sent to the backend)
33-
// const onAPIKeyChange = (event: ChangeEvent<HTMLInputElement>) => {
34-
// console.log(event, options);
35-
// onOptionsChange({
36-
// ...options,
37-
// secureJsonData: {
38-
// apiKey: event.target.value,
39-
// },
40-
// });
41-
// };
42-
43-
// const onHttpChange = (event: ChangeEvent<HTMLInputElement>) => {
44-
// console.log(event);
45-
// onOptionsChange({
46-
// ...options,
47-
// secureJsonData: {},
48-
// });
49-
// };
50-
51-
// const onResetAPIKey = () => {
52-
// onOptionsChange({
53-
// ...options,
54-
// secureJsonFields: {
55-
// ...options.secureJsonFields,
56-
// apiKey: false,
57-
// },
58-
// secureJsonData: {
59-
// ...options.secureJsonData,
60-
// apiKey: '',
61-
// },
62-
// });
63-
// };
64-
65-
// const secureJsonData = (options.secureJsonData || {}) as MySecureJsonData;
66-
67-
// const showAccessOptions = useRef(props.options.access === 'direct');
68-
6911
return (
70-
<div className="">
12+
<div>
7113
<DataSourceHttpSettings
7214
defaultUrl="http://localhost:9200"
7315
dataSourceConfig={options}
7416
showAccessOptions={false}
7517
onChange={onOptionsChange}
7618
/>
77-
{/*
78-
<InlineField label="Path" labelWidth={12}>
79-
<Input
80-
onChange={onPathChange}
81-
value={jsonData.path || ''}
82-
placeholder="json field returned to frontend"
83-
width={40}
84-
/>
85-
</InlineField>
86-
<InlineField label="API Key" labelWidth={12}>
87-
<SecretInput
88-
isConfigured={(secureJsonFields && secureJsonFields.apiKey) as boolean}
89-
value={secureJsonData.apiKey || ''}
90-
placeholder="secure json field (backend only)"
91-
width={40}
92-
onReset={onResetAPIKey}
93-
onChange={onAPIKeyChange}
94-
/>
95-
</InlineField> */}
9619
</div>
9720
);
9821
}

src/datasource.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { getBackendSrv } from '@grafana/runtime';
1212

1313
import { MyQuery, MyDataSourceOptions, TimeRange } from './types';
14-
import { b64EncodeUnicode } from 'utils/zincutils';
14+
import { b64EncodeUnicode, logsErrorMessage } from 'utils/zincutils';
1515
export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
1616
instanceSettings?: DataSourceInstanceSettings<MyDataSourceOptions>;
1717
url: string;
@@ -36,7 +36,7 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
3636
const promises = options.targets.map((target) => {
3737
// Your code goes here.
3838
const reqData = this.buildQuery(target, timestamps);
39-
return this.doRequest(reqData)
39+
return this.doRequest(target, reqData)
4040
.then((response) => {
4141
const frame = new MutableDataFrame({
4242
refId: target.refId,
@@ -54,7 +54,7 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
5454
{ name: 'kubernetes_host', type: FieldType.string },
5555
],
5656
});
57-
response.data.hits.forEach((point: any) => {
57+
response.hits.forEach((point: any) => {
5858
frame.appendRow([
5959
point.time,
6060
point.log,
@@ -68,24 +68,30 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
6868
});
6969
return frame;
7070
})
71-
.catch((err) => console.log(err));
71+
.catch((err) => {
72+
let error = '';
73+
if (err.response !== undefined) {
74+
error = err.response.data.error;
75+
} else {
76+
error = err.message;
77+
}
78+
79+
const customMessage = logsErrorMessage(err.response.data.code);
80+
if (customMessage) {
81+
error = customMessage;
82+
}
83+
throw new Error(error);
84+
});
7285
});
7386

7487
return Promise.all(promises).then((data) => {
7588
return { data: data || [] };
7689
});
7790
}
7891

79-
async doRequest(data: any) {
80-
const headers: any = {};
81-
headers['Content-Type'] = 'application/x-ndjson';
82-
return getBackendSrv().datasourceRequest({
83-
method: 'POST',
84-
url: this.url + '/_search',
85-
params: {
86-
type: 'logs',
87-
},
88-
data,
92+
doRequest(target: any, data: any) {
93+
return getBackendSrv().post(this.url + `/${target.organization}/_search?type=logs`, data, {
94+
showErrorAlert: false,
8995
});
9096
}
9197

0 commit comments

Comments
 (0)