mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Live: add rule for datasource (#40738)
This commit is contained in:
parent
1095f69740
commit
4680a8454f
@ -317,9 +317,6 @@ 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",
|
||||
@ -328,6 +325,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
|
||||
Url: hs.Cfg.AppSubURL + "/live",
|
||||
Children: liveNavLinks,
|
||||
HideFromMenu: true,
|
||||
HideFromTabs: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,48 @@
|
||||
import React from 'react';
|
||||
import { Input, Form, Field, Button } from '@grafana/ui';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import React, { useState } from 'react';
|
||||
import { Input, Field, Button, ValuePicker, HorizontalGroup } from '@grafana/ui';
|
||||
import { DataSourcePicker, getBackendSrv } from '@grafana/runtime';
|
||||
import { AppEvents, DatasourceRef, LiveChannelScope, SelectableValue } from '@grafana/data';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { Rule } from './types';
|
||||
|
||||
interface Props {
|
||||
onClose: (state: boolean) => void;
|
||||
onRuleAdded: (rule: Rule) => void;
|
||||
}
|
||||
export function AddNewRule({ onClose }: Props) {
|
||||
const onSubmit = (formData: Rule) => {
|
||||
|
||||
type PatternType = 'ds' | 'any';
|
||||
|
||||
const patternTypes: Array<SelectableValue<PatternType>> = [
|
||||
{
|
||||
label: 'Data source',
|
||||
description: 'Configure a channel scoped to a data source instance',
|
||||
value: 'ds',
|
||||
},
|
||||
{
|
||||
label: 'Any',
|
||||
description: 'Enter an arbitray channel pattern',
|
||||
value: 'any',
|
||||
},
|
||||
];
|
||||
|
||||
export function AddNewRule({ onRuleAdded }: Props) {
|
||||
const [patternType, setPatternType] = useState<PatternType>();
|
||||
const [pattern, setPattern] = useState<string>();
|
||||
const [patternPrefix, setPatternPrefix] = useState<string>('');
|
||||
const [datasource, setDatasource] = useState<DatasourceRef>();
|
||||
|
||||
const onSubmit = () => {
|
||||
if (!pattern) {
|
||||
appEvents.emit(AppEvents.alertError, ['Enter path']);
|
||||
return;
|
||||
}
|
||||
if (patternType === 'ds' && !patternPrefix.length) {
|
||||
appEvents.emit(AppEvents.alertError, ['Select datasource']);
|
||||
return;
|
||||
}
|
||||
|
||||
getBackendSrv()
|
||||
.post(`api/live/channel-rules`, {
|
||||
pattern: formData.pattern,
|
||||
settings: {
|
||||
output: formData.settings.frameOutputs,
|
||||
converter: formData.settings.converter,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
// close modal
|
||||
onClose(false);
|
||||
})
|
||||
.catch((e) => console.error(e));
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
defaultValues={{
|
||||
pattern: '',
|
||||
pattern: patternPrefix + pattern,
|
||||
settings: {
|
||||
converter: {
|
||||
type: 'jsonAuto',
|
||||
@ -37,17 +53,77 @@ export function AddNewRule({ onClose }: Props) {
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{({ register, errors }) => (
|
||||
<>
|
||||
<Field label="Pattern" invalid={!!errors.pattern} error="Pattern is required">
|
||||
<Input {...register('pattern', { required: true })} placeholder="scope/namespace/path" />
|
||||
})
|
||||
.then((v: any) => {
|
||||
console.log('ADDED', v);
|
||||
setPattern(undefined);
|
||||
setPatternType(undefined);
|
||||
onRuleAdded(v.rule);
|
||||
})
|
||||
.catch((e) => {
|
||||
appEvents.emit(AppEvents.alertError, ['Error adding rule', e]);
|
||||
e.isHandled = true;
|
||||
});
|
||||
};
|
||||
|
||||
if (patternType) {
|
||||
return (
|
||||
<div>
|
||||
<HorizontalGroup>
|
||||
{patternType === 'any' && (
|
||||
<Field label="Pattern">
|
||||
<Input
|
||||
value={pattern ?? ''}
|
||||
onChange={(e) => setPattern(e.currentTarget.value)}
|
||||
placeholder="scope/namespace/path"
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{patternType === 'ds' && (
|
||||
<>
|
||||
<Field label="Data source">
|
||||
<DataSourcePicker
|
||||
current={datasource}
|
||||
onChange={(ds) => {
|
||||
setDatasource(ds.name);
|
||||
setPatternPrefix(`${LiveChannelScope.DataSource}/${ds.uid}/`);
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
<Field label="Path">
|
||||
<Input value={pattern ?? ''} onChange={(e) => setPattern(e.currentTarget.value)} placeholder="path" />
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Field label="">
|
||||
<Button onClick={onSubmit} variant={pattern?.length ? 'primary' : 'secondary'}>
|
||||
Add
|
||||
</Button>
|
||||
</Field>
|
||||
<Button>Add</Button>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
|
||||
<Field label="">
|
||||
<Button variant="secondary" onClick={() => setPatternType(undefined)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Field>
|
||||
</HorizontalGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ValuePicker
|
||||
label="Add channel rule"
|
||||
variant="secondary"
|
||||
size="md"
|
||||
icon="plus"
|
||||
menuPlacement="auto"
|
||||
isFullWidth={false}
|
||||
options={patternTypes}
|
||||
onChange={(v) => setPatternType(v.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,33 +1,20 @@
|
||||
import React, { useEffect, useState, ChangeEvent } from 'react';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Input, Tag, useStyles, Button, Modal, IconButton } from '@grafana/ui';
|
||||
import { Input } from '@grafana/ui';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { useNavModel } from 'app/core/hooks/useNavModel';
|
||||
import { css } from '@emotion/css';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Rule, Output, RuleType } from './types';
|
||||
import { RuleModal } from './RuleModal';
|
||||
import { Rule } from './types';
|
||||
import { PipelineTable } from './PipelineTable';
|
||||
import { AddNewRule } from './AddNewRule';
|
||||
|
||||
function renderOutputTags(key: string, output?: Output): React.ReactNode {
|
||||
if (!output?.type) {
|
||||
return null;
|
||||
}
|
||||
return <Tag key={key} name={output.type} />;
|
||||
}
|
||||
|
||||
export default function PipelineAdminPage() {
|
||||
const [rules, setRules] = useState<Rule[]>([]);
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [selectedRule, setSelectedRule] = useState<Rule>();
|
||||
const [defaultRules, setDefaultRules] = useState<any[]>([]);
|
||||
const [newRule, setNewRule] = useState<Rule>();
|
||||
const navModel = useNavModel('live-pipeline');
|
||||
const [isOpenEditor, setOpenEditor] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string>();
|
||||
const [clickColumn, setClickColumn] = useState<RuleType>('converter');
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
useEffect(() => {
|
||||
const loadRules = () => {
|
||||
getBackendSrv()
|
||||
.get(`api/live/channel-rules`)
|
||||
.then((data) => {
|
||||
@ -39,20 +26,12 @@ export default function PipelineAdminPage() {
|
||||
setError(JSON.stringify(e.data, null, 2));
|
||||
}
|
||||
});
|
||||
}, [isOpenEditor, isOpen]);
|
||||
|
||||
const onRowClick = (event: any) => {
|
||||
const pattern = event.target.getAttribute('data-pattern');
|
||||
const column = event.target.getAttribute('data-column');
|
||||
if (column === 'pattern') {
|
||||
setClickColumn('converter');
|
||||
} else {
|
||||
setClickColumn(column);
|
||||
}
|
||||
setSelectedRule(rules.filter((rule) => rule.pattern === pattern)[0]);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadRules();
|
||||
}, []);
|
||||
|
||||
const onSearchQueryChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.value) {
|
||||
setRules(rules.filter((rule) => rule.pattern.toLowerCase().includes(e.target.value.toLowerCase())));
|
||||
@ -61,11 +40,6 @@ export default function PipelineAdminPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const onRemoveRule = (pattern: string) => {
|
||||
getBackendSrv()
|
||||
.delete(`api/live/channel-rules`, JSON.stringify({ pattern: pattern }))
|
||||
.catch((e) => console.error(e));
|
||||
};
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
@ -73,75 +47,19 @@ export default function PipelineAdminPage() {
|
||||
<div className="page-action-bar">
|
||||
<div className="gf-form gf-form--grow">
|
||||
<Input placeholder="Search pattern..." onChange={onSearchQueryChange} />
|
||||
<Button className={styles.addNew} onClick={() => setOpenEditor(true)}>
|
||||
Add Rule
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="admin-list-table">
|
||||
<table className="filter-table filter-table--hover form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Pattern</th>
|
||||
<th>Converter</th>
|
||||
<th>Processor</th>
|
||||
<th>Output</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rules.map((rule) => (
|
||||
<tr key={rule.pattern} onClick={onRowClick} className={styles.row}>
|
||||
<td data-pattern={rule.pattern} data-column="pattern">
|
||||
{rule.pattern}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="converter">
|
||||
{rule.settings?.converter?.type}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="processor">
|
||||
{rule.settings?.frameProcessors?.map((processor) => (
|
||||
<span key={rule.pattern + processor.type}>{processor.type}</span>
|
||||
))}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="output">
|
||||
{rule.settings?.frameOutputs?.map((output) => (
|
||||
<span key={rule.pattern + output.type}>{renderOutputTags('out', output)}</span>
|
||||
))}
|
||||
</td>
|
||||
<td>
|
||||
<IconButton name="trash-alt" onClick={() => onRemoveRule(rule.pattern)}></IconButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{isOpenEditor && (
|
||||
<Modal isOpen={isOpenEditor} onDismiss={() => setOpenEditor(false)} title="Add a new rule">
|
||||
<AddNewRule onClose={setOpenEditor} />
|
||||
</Modal>
|
||||
)}
|
||||
{isOpen && selectedRule && (
|
||||
<RuleModal
|
||||
rule={selectedRule}
|
||||
isOpen={isOpen}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
clickColumn={clickColumn}
|
||||
/>
|
||||
)}
|
||||
|
||||
<PipelineTable rules={rules} onRuleChanged={loadRules} selectRule={newRule} />
|
||||
|
||||
<AddNewRule
|
||||
onRuleAdded={(r: Rule) => {
|
||||
console.log('GOT', r, 'vs', rules[0]);
|
||||
setNewRule(r);
|
||||
loadRules();
|
||||
}}
|
||||
/>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme) => {
|
||||
return {
|
||||
row: css`
|
||||
cursor: pointer;
|
||||
`,
|
||||
addNew: css`
|
||||
margin-left: 10px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
144
public/app/features/live/pages/PipelineTable.tsx
Normal file
144
public/app/features/live/pages/PipelineTable.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { Tag, useStyles, IconButton } from '@grafana/ui';
|
||||
import { css } from '@emotion/css';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Rule, Output, RuleType } from './types';
|
||||
import { RuleModal } from './RuleModal';
|
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
|
||||
|
||||
function renderOutputTags(key: string, output?: Output): React.ReactNode {
|
||||
if (!output?.type) {
|
||||
return null;
|
||||
}
|
||||
return <Tag key={key} name={output.type} />;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
rules: Rule[];
|
||||
onRuleChanged: () => void;
|
||||
selectRule?: Rule;
|
||||
}
|
||||
|
||||
export const PipelineTable: React.FC<Props> = (props) => {
|
||||
const { rules } = props;
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [selectedRule, setSelectedRule] = useState<Rule>();
|
||||
const [clickColumn, setClickColumn] = useState<RuleType>('converter');
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
const onRowClick = (rule: Rule, event?: any) => {
|
||||
if (!rule) {
|
||||
return;
|
||||
}
|
||||
let column = event?.target?.getAttribute('data-column');
|
||||
if (!column || column === 'pattern') {
|
||||
column = 'converter';
|
||||
}
|
||||
setClickColumn(column);
|
||||
setSelectedRule(rule);
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
// Supports selecting a rule from external config (after add rule)
|
||||
useEffect(() => {
|
||||
if (props.selectRule) {
|
||||
onRowClick(props.selectRule);
|
||||
}
|
||||
}, [props.selectRule]);
|
||||
|
||||
const onRemoveRule = (pattern: string) => {
|
||||
getBackendSrv()
|
||||
.delete(`api/live/channel-rules`, JSON.stringify({ pattern: pattern }))
|
||||
.catch((e) => console.error(e))
|
||||
.finally(() => {
|
||||
props.onRuleChanged();
|
||||
});
|
||||
};
|
||||
|
||||
const renderPattern = (pattern: string) => {
|
||||
if (pattern.startsWith('ds/')) {
|
||||
const idx = pattern.indexOf('/', 4);
|
||||
if (idx > 3) {
|
||||
const uid = pattern.substring(3, idx);
|
||||
const ds = getDatasourceSrv().getInstanceSettings(uid);
|
||||
if (ds) {
|
||||
return (
|
||||
<div>
|
||||
<Tag name={ds.name} colorIndex={1} />
|
||||
<span>{pattern.substring(idx + 1)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pattern;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="admin-list-table">
|
||||
<table className="filter-table filter-table--hover form-inline">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Channel</th>
|
||||
<th>Converter</th>
|
||||
<th>Processor</th>
|
||||
<th>Output</th>
|
||||
<th style={{ width: 10 }}> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rules.map((rule) => (
|
||||
<tr key={rule.pattern} onClick={(e) => onRowClick(rule, e)} className={styles.row}>
|
||||
<td data-pattern={rule.pattern} data-column="pattern">
|
||||
{renderPattern(rule.pattern)}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="converter">
|
||||
{rule.settings?.converter?.type}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="processor">
|
||||
{rule.settings?.frameProcessors?.map((processor) => (
|
||||
<span key={rule.pattern + processor.type}>{processor.type}</span>
|
||||
))}
|
||||
</td>
|
||||
<td data-pattern={rule.pattern} data-column="output">
|
||||
{rule.settings?.frameOutputs?.map((output) => (
|
||||
<span key={rule.pattern + output.type}>{renderOutputTags('out', output)}</span>
|
||||
))}
|
||||
</td>
|
||||
<td>
|
||||
<IconButton
|
||||
name="trash-alt"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRemoveRule(rule.pattern);
|
||||
}}
|
||||
></IconButton>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{isOpen && selectedRule && (
|
||||
<RuleModal
|
||||
rule={selectedRule}
|
||||
isOpen={isOpen}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
clickColumn={clickColumn}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme) => {
|
||||
return {
|
||||
row: css`
|
||||
cursor: pointer;
|
||||
`,
|
||||
};
|
||||
};
|
@ -7,6 +7,7 @@ import { GrafanaTheme } from '@grafana/data';
|
||||
import { RuleSettingsEditor } from './RuleSettingsEditor';
|
||||
import { getPipeLineEntities } from './utils';
|
||||
import { RuleSettingsArray } from './RuleSettingsArray';
|
||||
import { RuleTest } from './RuleTest';
|
||||
|
||||
interface Props {
|
||||
rule: Rule;
|
||||
@ -14,35 +15,41 @@ interface Props {
|
||||
onClose: () => void;
|
||||
clickColumn: RuleType;
|
||||
}
|
||||
interface TabType {
|
||||
interface TabInfo {
|
||||
label: string;
|
||||
value: RuleType;
|
||||
type?: RuleType;
|
||||
isTest?: boolean;
|
||||
isConverter?: boolean;
|
||||
icon?: string;
|
||||
}
|
||||
const tabs: TabType[] = [
|
||||
{ label: 'Converter', value: 'converter' },
|
||||
{ label: 'Processors', value: 'frameProcessors' },
|
||||
{ label: 'Outputs', value: 'frameOutputs' },
|
||||
const tabs: TabInfo[] = [
|
||||
{ label: 'Converter', type: 'converter', isConverter: true },
|
||||
{ label: 'Processors', type: 'frameProcessors' },
|
||||
{ label: 'Outputs', type: 'frameOutputs' },
|
||||
{ label: 'Test', isTest: true, icon: 'flask' },
|
||||
];
|
||||
|
||||
export const RuleModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, clickColumn } = props;
|
||||
const [rule, setRule] = useState<Rule>(props.rule);
|
||||
const [activeTab, setActiveTab] = useState<RuleType>(clickColumn);
|
||||
const [activeTab, setActiveTab] = useState<TabInfo | undefined>(tabs.find((t) => t.type === clickColumn));
|
||||
// to show color of Save button
|
||||
const [hasChange, setChange] = useState<boolean>(false);
|
||||
const [ruleSetting, setRuleSetting] = useState<any>(rule?.settings?.[activeTab]);
|
||||
const [ruleSetting, setRuleSetting] = useState<any>(activeTab?.type ? rule?.settings?.[activeTab.type] : undefined);
|
||||
const [entitiesInfo, setEntitiesInfo] = useState<PipeLineEntitiesInfo>();
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
const onRuleSettingChange = (value: RuleSetting | RuleSetting[]) => {
|
||||
setChange(true);
|
||||
setRule({
|
||||
...rule,
|
||||
settings: {
|
||||
...rule.settings,
|
||||
[activeTab]: value,
|
||||
},
|
||||
});
|
||||
if (activeTab?.type) {
|
||||
setRule({
|
||||
...rule,
|
||||
settings: {
|
||||
...rule.settings,
|
||||
[activeTab?.type]: value,
|
||||
},
|
||||
});
|
||||
}
|
||||
setRuleSetting(value);
|
||||
};
|
||||
|
||||
@ -71,32 +78,40 @@ export const RuleModal: React.FC<Props> = (props) => {
|
||||
<Tab
|
||||
key={index}
|
||||
label={tab.label}
|
||||
active={tab.value === activeTab}
|
||||
active={tab === activeTab}
|
||||
icon={tab.icon as any}
|
||||
onChangeTab={() => {
|
||||
setActiveTab(tab.value);
|
||||
// to notify children of the new rule
|
||||
setRuleSetting(rule?.settings?.[tab.value]);
|
||||
setActiveTab(tab);
|
||||
if (tab.type) {
|
||||
// to notify children of the new rule
|
||||
setRuleSetting(rule?.settings?.[tab.type]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TabsBar>
|
||||
<TabContent>
|
||||
{entitiesInfo && rule && activeTab === 'converter' && (
|
||||
<RuleSettingsEditor
|
||||
onChange={onRuleSettingChange}
|
||||
value={ruleSetting}
|
||||
ruleType={activeTab}
|
||||
entitiesInfo={entitiesInfo}
|
||||
/>
|
||||
)}
|
||||
{entitiesInfo && rule && activeTab !== 'converter' && (
|
||||
<RuleSettingsArray
|
||||
onChange={onRuleSettingChange}
|
||||
value={ruleSetting}
|
||||
ruleType={activeTab}
|
||||
entitiesInfo={entitiesInfo}
|
||||
/>
|
||||
{entitiesInfo && rule && activeTab && (
|
||||
<>
|
||||
{activeTab?.isTest && <RuleTest rule={rule} />}
|
||||
{activeTab.isConverter && (
|
||||
<RuleSettingsEditor
|
||||
onChange={onRuleSettingChange}
|
||||
value={ruleSetting}
|
||||
ruleType={'converter'}
|
||||
entitiesInfo={entitiesInfo}
|
||||
/>
|
||||
)}
|
||||
{!activeTab.isConverter && activeTab.type && (
|
||||
<RuleSettingsArray
|
||||
onChange={onRuleSettingChange}
|
||||
value={ruleSetting}
|
||||
ruleType={activeTab.type}
|
||||
entitiesInfo={entitiesInfo}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button onClick={onSave} className={styles.save} variant={hasChange ? 'primary' : 'secondary'}>
|
||||
Save
|
||||
|
@ -1,33 +1,18 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Button, CodeEditor, Table, useStyles, Select, Field } from '@grafana/ui';
|
||||
import React, { useState } from 'react';
|
||||
import { Button, CodeEditor, Table, useStyles, 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');
|
||||
interface Props {
|
||||
rule: Rule;
|
||||
}
|
||||
|
||||
export const RuleTest: React.FC<Props> = (props) => {
|
||||
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);
|
||||
@ -36,8 +21,8 @@ export default function RuleTest() {
|
||||
const onClick = () => {
|
||||
getBackendSrv()
|
||||
.post(`api/live/pipeline-convert-test`, {
|
||||
channelRules: channelRules,
|
||||
channel: channelSelected,
|
||||
channelRules: [props.rule],
|
||||
channel: props.rule.pattern,
|
||||
data: data,
|
||||
})
|
||||
.then((data: any) => {
|
||||
@ -60,45 +45,31 @@ export default function RuleTest() {
|
||||
};
|
||||
|
||||
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>
|
||||
<div>
|
||||
<CodeEditor
|
||||
height={100}
|
||||
value=""
|
||||
showLineNumbers={true}
|
||||
readOnly={false}
|
||||
language="json"
|
||||
showMiniMap={false}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
|
||||
{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>
|
||||
<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={700} height={Math.min(10 * r.frame.length + 10, 150)} showTypeIcons></Table>
|
||||
</Field>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme) => {
|
||||
return {
|
||||
margin: css`
|
||||
|
@ -22,12 +22,6 @@ 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[] {
|
||||
|
Loading…
Reference in New Issue
Block a user