Live: Test Converter tab (#40536)

This commit is contained in:
An 2021-10-19 03:50:17 -04:00 committed by GitHub
parent d179c2a4e9
commit f4e78ea27b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 11 deletions

View File

@ -314,7 +314,9 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
liveNavLinks = append(liveNavLinks, &dtos.NavLink{
Text: "Cloud", Id: "live-cloud", Url: hs.Cfg.AppSubURL + "/live/cloud", Icon: "cloud-upload",
})
liveNavLinks = append(liveNavLinks, &dtos.NavLink{
Text: "Test", Id: "live-test", Url: hs.Cfg.AppSubURL + "/live/test", Icon: "arrow",
})
navTree = append(navTree, &dtos.NavLink{
Id: "live",
Text: "Live",

View File

@ -58,7 +58,9 @@ var ConvertersRegistry = []EntityInfo{
{
Type: ConverterTypeInfluxAuto,
Description: "accept influx line protocol",
Example: AutoInfluxConverterConfig{},
Example: AutoInfluxConverterConfig{
FrameFormat: "labels_column",
},
},
{
Type: ConverterTypeJsonFrame,

View File

@ -16,16 +16,19 @@ type FileStorage struct {
}
func (f *FileStorage) ListRemoteWriteBackends(_ context.Context, orgID int64) ([]RemoteWriteBackend, error) {
backendBytes, err := ioutil.ReadFile(filepath.Join(f.DataPath, "pipeline", "remote-write-backends.json"))
cfgfile := filepath.Join(f.DataPath, "pipeline", "remote-write-backends.json")
var backends []RemoteWriteBackend
// Safe to ignore gosec warning G304.
// nolint:gosec
backendBytes, err := ioutil.ReadFile(cfgfile)
if err != nil {
return nil, fmt.Errorf("can't read ./pipeline/remote-write-backends.json file: %w", err)
return backends, fmt.Errorf("can't read %s file: %w", cfgfile, err)
}
var remoteWriteBackends RemoteWriteBackends
err = json.Unmarshal(backendBytes, &remoteWriteBackends)
if err != nil {
return nil, fmt.Errorf("can't unmarshal remote-write-backends.json data: %w", err)
}
var backends []RemoteWriteBackend
for _, b := range remoteWriteBackends.Backends {
if b.OrgId == orgID || (orgID == 1 && b.OrgId == 0) {
backends = append(backends, b)
@ -114,7 +117,7 @@ func (f *FileStorage) readRules() (ChannelRules, error) {
// nolint:gosec
ruleBytes, err := ioutil.ReadFile(ruleFile)
if err != nil {
return ChannelRules{}, fmt.Errorf("can't read ./data/live-channel-rules.json file: %w", err)
return ChannelRules{}, fmt.Errorf("can't read pipeline rules: %s: %w", f.ruleFilePath(), err)
}
var channelRules ChannelRules
err = json.Unmarshal(ruleBytes, &channelRules)

View File

@ -31,8 +31,8 @@ export default function PipelineAdminPage() {
getBackendSrv()
.get(`api/live/channel-rules`)
.then((data) => {
setRules(data.rules);
setDefaultRules(data.rules);
setRules(data.rules ?? []);
setDefaultRules(data.rules ?? []);
})
.catch((e) => {
if (e.data) {

View File

@ -0,0 +1,108 @@
import React, { useState, useEffect } from 'react';
import { Button, CodeEditor, Table, useStyles, Select, Field } from '@grafana/ui';
import { ChannelFrame, Rule } from './types';
import Page from 'app/core/components/Page/Page';
import { useNavModel } from 'app/core/hooks/useNavModel';
import { getBackendSrv, config } from '@grafana/runtime';
import { css } from '@emotion/css';
import { getDisplayProcessor, GrafanaTheme, StreamingDataFrame } from '@grafana/data';
import { transformLabel } from './utils';
export default function RuleTest() {
const navModel = useNavModel('live-test');
const [response, setResponse] = useState<ChannelFrame[]>();
const [data, setData] = useState<string>();
const [rules, setRules] = useState<Rule[]>([]);
const [channelRules, setChannelRules] = useState<Rule[]>();
const [channelSelected, setChannelSelected] = useState<string>();
const styles = useStyles(getStyles);
useEffect(() => {
getBackendSrv()
.get(`api/live/channel-rules`)
.then((data) => {
setRules(data.rules);
})
.catch((e) => {
if (e.data) {
console.log(e);
}
});
}, []);
const onBlur = (text: string) => {
setData(text);
};
const onClick = () => {
getBackendSrv()
.post(`api/live/pipeline-convert-test`, {
channelRules: channelRules,
channel: channelSelected,
data: data,
})
.then((data: any) => {
const t = data.channelFrames as any[];
if (t) {
setResponse(
t.map((f) => {
const frame = new StreamingDataFrame(f.frame);
for (const field of frame.fields) {
field.display = getDisplayProcessor({ field, theme: config.theme2 });
}
return { channel: f.channel, frame };
})
);
}
})
.catch((e) => {
setResponse(e);
});
};
return (
<Page navModel={navModel}>
<Page.Contents>
<Field label="Channel">
<Select
menuShouldPortal
options={transformLabel(rules, 'pattern')}
value=""
onChange={(v) => {
setChannelSelected(v.value);
setChannelRules(rules.filter((r) => r.pattern === v.value));
}}
placeholder="Select Channel"
/>
</Field>
<Field label="Data">
<CodeEditor
height={200}
value=""
showLineNumbers={true}
readOnly={false}
language="json"
showMiniMap={false}
onBlur={onBlur}
/>
</Field>
<Button onClick={onClick} className={styles.margin}>
Test
</Button>
{response?.length &&
response.map((r) => (
<Field key={r.channel} label={r.channel}>
<Table data={r.frame} width={650} height={10 * r.frame.length + 10} showTypeIcons></Table>
</Field>
))}
</Page.Contents>
</Page>
);
}
const getStyles = (theme: GrafanaTheme) => {
return {
margin: css`
margin-bottom: 15px;
`,
};
};

View File

@ -22,6 +22,12 @@ const liveRoutes = [
() => import(/* webpackChunkName: "CloudAdminPage" */ 'app/features/live/pages/CloudAdminPage')
),
},
{
path: '/live/test',
component: SafeDynamicImport(
() => import(/* webpackChunkName: "CloudAdminPage" */ 'app/features/live/pages/RuleTest')
),
},
];
export function getLiveRoutes(cfg = config): RouteDescriptor[] {

View File

@ -1,4 +1,4 @@
import { SelectableValue } from '@grafana/data';
import { DataFrame, SelectableValue } from '@grafana/data';
export interface Converter extends RuleSetting {
[t: string]: any;
}
@ -54,3 +54,8 @@ export interface PipeLineEntitiesInfo {
frameOutputs: SelectableValue[];
getExample: (rule: RuleType, type: string) => object;
}
export interface ChannelFrame {
channel: string;
frame: DataFrame;
}

View File

@ -1,5 +1,5 @@
import { getBackendSrv } from '@grafana/runtime';
import { PipelineListOption, EntitiesTypes, PipeLineEntitiesInfo } from './types';
import { PipelineListOption, PipeLineEntitiesInfo } from './types';
export async function getPipeLineEntities(): Promise<PipeLineEntitiesInfo> {
return await getBackendSrv()
@ -16,7 +16,13 @@ export async function getPipeLineEntities(): Promise<PipeLineEntitiesInfo> {
});
}
function transformLabel(data: EntitiesTypes, key: keyof typeof data) {
export function transformLabel(data: any, key: keyof typeof data) {
if (Array.isArray(data)) {
return data.map((d) => ({
label: d[key],
value: d[key],
}));
}
return data[key].map((typeObj: PipelineListOption) => ({
label: typeObj.type,
value: typeObj.type,