mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Live: Test Converter tab (#40536)
This commit is contained in:
parent
d179c2a4e9
commit
f4e78ea27b
@ -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",
|
||||
|
@ -58,7 +58,9 @@ var ConvertersRegistry = []EntityInfo{
|
||||
{
|
||||
Type: ConverterTypeInfluxAuto,
|
||||
Description: "accept influx line protocol",
|
||||
Example: AutoInfluxConverterConfig{},
|
||||
Example: AutoInfluxConverterConfig{
|
||||
FrameFormat: "labels_column",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ConverterTypeJsonFrame,
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
108
public/app/features/live/pages/RuleTest.tsx
Normal file
108
public/app/features/live/pages/RuleTest.tsx
Normal 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;
|
||||
`,
|
||||
};
|
||||
};
|
@ -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[] {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user