mirror of https://github.com/grafana/grafana
Live: Remove (alpha) ability to configure live pipelines (#65138)
parent
b2fb7a162a
commit
f96637b5fc
|
@ -1,132 +0,0 @@ |
||||
import React, { useState } from 'react'; |
||||
|
||||
import { DataSourceRef, LiveChannelScope, SelectableValue } from '@grafana/data'; |
||||
import { DataSourcePicker, getBackendSrv } from '@grafana/runtime'; |
||||
import { Input, Field, Button, ValuePicker, HorizontalGroup } from '@grafana/ui'; |
||||
import { useAppNotification } from 'app/core/copy/appNotification'; |
||||
|
||||
import { Rule } from './types'; |
||||
|
||||
interface Props { |
||||
onRuleAdded: (rule: Rule) => void; |
||||
} |
||||
|
||||
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 notifyApp = useAppNotification(); |
||||
|
||||
const onSubmit = () => { |
||||
if (!pattern) { |
||||
notifyApp.error('Enter path'); |
||||
return; |
||||
} |
||||
if (patternType === 'ds' && !patternPrefix.length) { |
||||
notifyApp.error('Select datasource'); |
||||
return; |
||||
} |
||||
|
||||
getBackendSrv() |
||||
.post(`api/live/channel-rules`, { |
||||
pattern: patternPrefix + pattern, |
||||
settings: { |
||||
converter: { |
||||
type: 'jsonAuto', |
||||
}, |
||||
frameOutputs: [ |
||||
{ |
||||
type: 'managedStream', |
||||
}, |
||||
], |
||||
}, |
||||
}) |
||||
.then((v: any) => { |
||||
console.log('ADDED', v); |
||||
setPattern(undefined); |
||||
setPatternType(undefined); |
||||
onRuleAdded(v.rule); |
||||
}) |
||||
.catch((e) => { |
||||
notifyApp.error('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); |
||||
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> |
||||
|
||||
<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,51 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { useEffect, useState } from 'react'; |
||||
|
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
import { useNavModel } from 'app/core/hooks/useNavModel'; |
||||
|
||||
import { GrafanaCloudBackend } from './types'; |
||||
|
||||
export default function CloudAdminPage() { |
||||
const navModel = useNavModel('live-cloud'); |
||||
const [cloud, setCloud] = useState<GrafanaCloudBackend[]>([]); |
||||
const [error, setError] = useState<string>(); |
||||
|
||||
useEffect(() => { |
||||
getBackendSrv() |
||||
.get(`api/live/write-configs`) |
||||
.then((data) => { |
||||
setCloud(data.writeConfigs); |
||||
}) |
||||
.catch((e) => { |
||||
if (e.data) { |
||||
setError(JSON.stringify(e.data, null, 2)); |
||||
} |
||||
}); |
||||
}, []); |
||||
|
||||
return ( |
||||
<Page navModel={navModel}> |
||||
<Page.Contents> |
||||
{error && <pre>{error}</pre>} |
||||
{!cloud && <>Loading cloud definitions</>} |
||||
{cloud && |
||||
cloud.map((v) => { |
||||
return ( |
||||
<div key={v.uid}> |
||||
<h2>{v.uid}</h2> |
||||
<pre className={styles.row}>{JSON.stringify(v.settings, null, 2)}</pre> |
||||
</div> |
||||
); |
||||
})} |
||||
</Page.Contents> |
||||
</Page> |
||||
); |
||||
} |
||||
|
||||
const styles = { |
||||
row: css` |
||||
cursor: pointer; |
||||
`,
|
||||
}; |
@ -1,22 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
import { useNavModel } from 'app/core/hooks/useNavModel'; |
||||
|
||||
export default function FeatureTogglePage() { |
||||
const navModel = useNavModel('live-status'); |
||||
|
||||
return ( |
||||
<Page navModel={navModel}> |
||||
<Page.Contents> |
||||
<h1>Pipeline is not enabled</h1> |
||||
To enable pipelines, enable the feature toggle: |
||||
<pre> |
||||
{`[feature_toggles]
|
||||
enable = live-pipeline |
||||
`}
|
||||
</pre> |
||||
</Page.Contents> |
||||
</Page> |
||||
); |
||||
} |
@ -1,14 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
import { useNavModel } from 'app/core/hooks/useNavModel'; |
||||
|
||||
export default function CloudAdminPage() { |
||||
const navModel = useNavModel('live-status'); |
||||
|
||||
return ( |
||||
<Page navModel={navModel}> |
||||
<Page.Contents>Live/Live/Live</Page.Contents> |
||||
</Page> |
||||
); |
||||
} |
@ -1,67 +0,0 @@ |
||||
import React, { useEffect, useState, ChangeEvent } from 'react'; |
||||
|
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { Input } from '@grafana/ui'; |
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
import { useNavModel } from 'app/core/hooks/useNavModel'; |
||||
|
||||
import { AddNewRule } from './AddNewRule'; |
||||
import { PipelineTable } from './PipelineTable'; |
||||
import { Rule } from './types'; |
||||
|
||||
export default function PipelineAdminPage() { |
||||
const [rules, setRules] = useState<Rule[]>([]); |
||||
const [defaultRules, setDefaultRules] = useState<any[]>([]); |
||||
const [newRule, setNewRule] = useState<Rule>(); |
||||
const navModel = useNavModel('live-pipeline'); |
||||
const [error, setError] = useState<string>(); |
||||
|
||||
const loadRules = () => { |
||||
getBackendSrv() |
||||
.get(`api/live/channel-rules`) |
||||
.then((data) => { |
||||
setRules(data.rules ?? []); |
||||
setDefaultRules(data.rules ?? []); |
||||
}) |
||||
.catch((e) => { |
||||
if (e.data) { |
||||
setError(JSON.stringify(e.data, null, 2)); |
||||
} |
||||
}); |
||||
}; |
||||
|
||||
useEffect(() => { |
||||
loadRules(); |
||||
}, []); |
||||
|
||||
const onSearchQueryChange = (e: ChangeEvent<HTMLInputElement>) => { |
||||
if (e.target.value) { |
||||
setRules(rules.filter((rule) => rule.pattern.toLowerCase().includes(e.target.value.toLowerCase()))); |
||||
} else { |
||||
setRules(defaultRules); |
||||
} |
||||
}; |
||||
|
||||
return ( |
||||
<Page navModel={navModel}> |
||||
<Page.Contents> |
||||
{error && <pre>{error}</pre>} |
||||
<div className="page-action-bar"> |
||||
<div className="gf-form gf-form--grow"> |
||||
<Input placeholder="Search pattern..." onChange={onSearchQueryChange} /> |
||||
</div> |
||||
</div> |
||||
|
||||
<PipelineTable rules={rules} onRuleChanged={loadRules} selectRule={newRule} /> |
||||
|
||||
<AddNewRule |
||||
onRuleAdded={(r: Rule) => { |
||||
console.log('GOT', r, 'vs', rules[0]); |
||||
setNewRule(r); |
||||
loadRules(); |
||||
}} |
||||
/> |
||||
</Page.Contents> |
||||
</Page> |
||||
); |
||||
} |
@ -1,142 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { useEffect, useState } from 'react'; |
||||
|
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { Tag, IconButton } from '@grafana/ui'; |
||||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; |
||||
|
||||
import { RuleModal } from './RuleModal'; |
||||
import { Rule, Output, RuleType } from './types'; |
||||
|
||||
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 = (props: Props) => { |
||||
const { rules } = props; |
||||
const [isOpen, setOpen] = useState(false); |
||||
const [selectedRule, setSelectedRule] = useState<Rule>(); |
||||
const [clickColumn, setClickColumn] = useState<RuleType>('converter'); |
||||
|
||||
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 styles = { |
||||
row: css` |
||||
cursor: pointer; |
||||
`,
|
||||
}; |
@ -1,128 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { useState, useMemo } from 'react'; |
||||
|
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { Modal, TabContent, TabsBar, Tab, Button } from '@grafana/ui'; |
||||
|
||||
import { RuleSettingsArray } from './RuleSettingsArray'; |
||||
import { RuleSettingsEditor } from './RuleSettingsEditor'; |
||||
import { RuleTest } from './RuleTest'; |
||||
import { Rule, RuleType, PipeLineEntitiesInfo, RuleSetting } from './types'; |
||||
import { getPipeLineEntities } from './utils'; |
||||
|
||||
interface Props { |
||||
rule: Rule; |
||||
isOpen: boolean; |
||||
onClose: () => void; |
||||
clickColumn: RuleType; |
||||
} |
||||
interface TabInfo { |
||||
label: string; |
||||
type?: RuleType; |
||||
isTest?: boolean; |
||||
isConverter?: boolean; |
||||
icon?: string; |
||||
} |
||||
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 = (props: Props) => { |
||||
const { isOpen, onClose, clickColumn } = props; |
||||
const [rule, setRule] = useState<Rule>(props.rule); |
||||
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>(activeTab?.type ? rule?.settings?.[activeTab.type] : undefined); |
||||
const [entitiesInfo, setEntitiesInfo] = useState<PipeLineEntitiesInfo>(); |
||||
|
||||
const onRuleSettingChange = (value: RuleSetting | RuleSetting[]) => { |
||||
setChange(true); |
||||
if (activeTab?.type) { |
||||
setRule({ |
||||
...rule, |
||||
settings: { |
||||
...rule.settings, |
||||
[activeTab?.type]: value, |
||||
}, |
||||
}); |
||||
} |
||||
setRuleSetting(value); |
||||
}; |
||||
|
||||
// load pipeline entities info
|
||||
useMemo(() => { |
||||
getPipeLineEntities().then((data) => { |
||||
setEntitiesInfo(data); |
||||
}); |
||||
}, []); |
||||
|
||||
const onSave = () => { |
||||
getBackendSrv() |
||||
.put(`api/live/channel-rules`, rule) |
||||
.then(() => { |
||||
setChange(false); |
||||
onClose(); |
||||
}) |
||||
.catch((e) => console.error(e)); |
||||
}; |
||||
|
||||
return ( |
||||
<Modal isOpen={isOpen} title={rule.pattern} onDismiss={onClose} closeOnEscape> |
||||
<TabsBar> |
||||
{tabs.map((tab, index) => { |
||||
return ( |
||||
<Tab |
||||
key={index} |
||||
label={tab.label} |
||||
active={tab === activeTab} |
||||
icon={tab.icon as any} |
||||
onChangeTab={() => { |
||||
setActiveTab(tab); |
||||
if (tab.type) { |
||||
// to notify children of the new rule
|
||||
setRuleSetting(rule?.settings?.[tab.type]); |
||||
} |
||||
}} |
||||
/> |
||||
); |
||||
})} |
||||
</TabsBar> |
||||
<TabContent> |
||||
{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 |
||||
</Button> |
||||
</TabContent> |
||||
</Modal> |
||||
); |
||||
}; |
||||
|
||||
const styles = { |
||||
save: css` |
||||
margin-top: 5px; |
||||
`,
|
||||
}; |
@ -1,51 +0,0 @@ |
||||
import React, { useState } from 'react'; |
||||
|
||||
import { SelectableValue } from '@grafana/data'; |
||||
import { Select } from '@grafana/ui'; |
||||
|
||||
import { RuleSettingsEditor } from './RuleSettingsEditor'; |
||||
import { RuleType, RuleSetting, PipeLineEntitiesInfo } from './types'; |
||||
|
||||
interface Props { |
||||
ruleType: RuleType; |
||||
onChange: (value: RuleSetting[]) => void; |
||||
value: RuleSetting[]; |
||||
entitiesInfo: PipeLineEntitiesInfo; |
||||
} |
||||
|
||||
export const RuleSettingsArray = ({ onChange, value, ruleType, entitiesInfo }: Props) => { |
||||
const [index, setIndex] = useState<number>(0); |
||||
const arr = value ?? []; |
||||
const onRuleChange = (v: RuleSetting) => { |
||||
if (!value) { |
||||
onChange([v]); |
||||
} else { |
||||
const copy = [...value]; |
||||
copy[index] = v; |
||||
onChange(copy); |
||||
} |
||||
}; |
||||
// create array of value.length + 1
|
||||
let indexArr: Array<SelectableValue<number>> = []; |
||||
for (let i = 0; i <= arr.length; i++) { |
||||
indexArr.push({ |
||||
label: `${ruleType}: ${i}`, |
||||
value: i, |
||||
}); |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<Select |
||||
placeholder="Select an index" |
||||
options={indexArr} |
||||
value={index} |
||||
onChange={(index) => { |
||||
// set index to find the correct setting
|
||||
setIndex(index.value!); |
||||
}} |
||||
></Select> |
||||
<RuleSettingsEditor onChange={onRuleChange} value={arr[index]} ruleType={ruleType} entitiesInfo={entitiesInfo} /> |
||||
</> |
||||
); |
||||
}; |
@ -1,48 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { CodeEditor, Select } from '@grafana/ui'; |
||||
|
||||
import { RuleType, RuleSetting, PipeLineEntitiesInfo } from './types'; |
||||
|
||||
interface Props { |
||||
ruleType: RuleType; |
||||
onChange: (value: RuleSetting) => void; |
||||
value: RuleSetting; |
||||
entitiesInfo: PipeLineEntitiesInfo; |
||||
} |
||||
|
||||
export const RuleSettingsEditor = ({ onChange, value, ruleType, entitiesInfo }: Props) => { |
||||
return ( |
||||
<> |
||||
<Select |
||||
key={ruleType} |
||||
options={entitiesInfo[ruleType]} |
||||
placeholder="Select an option" |
||||
value={value?.type ?? ''} |
||||
onChange={(value) => { |
||||
// set the body with example
|
||||
const type = value.value; |
||||
onChange({ |
||||
type, |
||||
[type]: entitiesInfo.getExample(ruleType, type), |
||||
}); |
||||
}} |
||||
/> |
||||
<CodeEditor |
||||
height={'50vh'} |
||||
value={value ? JSON.stringify(value[value.type], null, '\t') : ''} |
||||
showLineNumbers={true} |
||||
readOnly={false} |
||||
language="json" |
||||
showMiniMap={false} |
||||
onBlur={(text: string) => { |
||||
const body = JSON.parse(text); |
||||
onChange({ |
||||
type: value.type, |
||||
[value.type]: body, |
||||
}); |
||||
}} |
||||
/> |
||||
</> |
||||
); |
||||
}; |
@ -1,78 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { useState } from 'react'; |
||||
|
||||
import { dataFrameFromJSON, getDisplayProcessor } from '@grafana/data'; |
||||
import { getBackendSrv, config } from '@grafana/runtime'; |
||||
import { Button, CodeEditor, Table, Field } from '@grafana/ui'; |
||||
|
||||
import { ChannelFrame, Rule } from './types'; |
||||
|
||||
interface Props { |
||||
rule: Rule; |
||||
} |
||||
|
||||
export const RuleTest = (props: Props) => { |
||||
const [response, setResponse] = useState<ChannelFrame[]>(); |
||||
const [data, setData] = useState<string>(); |
||||
|
||||
const onBlur = (text: string) => { |
||||
setData(text); |
||||
}; |
||||
|
||||
const onClick = () => { |
||||
getBackendSrv() |
||||
.post(`api/live/pipeline-convert-test`, { |
||||
channelRules: [props.rule], |
||||
channel: props.rule.pattern, |
||||
data: data, |
||||
}) |
||||
.then((data: any) => { |
||||
const t = data.channelFrames as any[]; |
||||
if (t) { |
||||
setResponse( |
||||
t.map((f) => { |
||||
const frame = dataFrameFromJSON(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 ( |
||||
<div> |
||||
<CodeEditor |
||||
height={100} |
||||
value="" |
||||
showLineNumbers={true} |
||||
readOnly={false} |
||||
language="json" |
||||
showMiniMap={false} |
||||
onBlur={onBlur} |
||||
/> |
||||
|
||||
<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 styles = { |
||||
margin: css` |
||||
margin-bottom: 15px; |
||||
`,
|
||||
}; |
@ -1,40 +0,0 @@ |
||||
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport'; |
||||
import { config } from 'app/core/config'; |
||||
import { RouteDescriptor } from 'app/core/navigation/types'; |
||||
import { isGrafanaAdmin } from 'app/features/plugins/admin/permissions'; |
||||
|
||||
const liveRoutes = [ |
||||
{ |
||||
path: '/live', |
||||
component: SafeDynamicImport( |
||||
() => import(/* webpackChunkName: "LiveStatusPage" */ 'app/features/live/pages/LiveStatusPage') |
||||
), |
||||
}, |
||||
{ |
||||
path: '/live/pipeline', |
||||
component: SafeDynamicImport( |
||||
() => import(/* webpackChunkName: "PipelineAdminPage" */ 'app/features/live/pages/PipelineAdminPage') |
||||
), |
||||
}, |
||||
{ |
||||
path: '/live/cloud', |
||||
component: SafeDynamicImport( |
||||
() => import(/* webpackChunkName: "CloudAdminPage" */ 'app/features/live/pages/CloudAdminPage') |
||||
), |
||||
}, |
||||
]; |
||||
|
||||
export function getLiveRoutes(cfg = config): RouteDescriptor[] { |
||||
if (!isGrafanaAdmin()) { |
||||
return []; |
||||
} |
||||
if (cfg.featureToggles['live-pipeline']) { |
||||
return liveRoutes; |
||||
} |
||||
return liveRoutes.map((v) => ({ |
||||
...v, |
||||
component: SafeDynamicImport( |
||||
() => import(/* webpackChunkName: "FeatureTogglePage" */ 'app/features/live/pages/FeatureTogglePage') |
||||
), |
||||
})); |
||||
} |
@ -1,61 +0,0 @@ |
||||
import { DataFrame, SelectableValue } from '@grafana/data'; |
||||
export interface Converter extends RuleSetting { |
||||
[t: string]: any; |
||||
} |
||||
|
||||
export interface Processor extends RuleSetting { |
||||
[t: string]: any; |
||||
} |
||||
|
||||
export interface Output extends RuleSetting { |
||||
[t: string]: any; |
||||
} |
||||
|
||||
export interface RuleSetting<T = any> { |
||||
type: string; |
||||
[key: string]: any; |
||||
} |
||||
export interface RuleSettings { |
||||
converter?: Converter; |
||||
frameProcessors?: Processor[]; |
||||
frameOutputs?: Output[]; |
||||
} |
||||
|
||||
export interface Rule { |
||||
pattern: string; |
||||
settings: RuleSettings; |
||||
} |
||||
|
||||
export interface Pipeline { |
||||
rules: Rule[]; |
||||
} |
||||
|
||||
export interface GrafanaCloudBackend { |
||||
uid: string; |
||||
settings: any; |
||||
} |
||||
|
||||
export type RuleType = 'converter' | 'frameProcessors' | 'frameOutputs'; |
||||
|
||||
export interface PipelineListOption { |
||||
type: string; |
||||
description: string; |
||||
example?: object; |
||||
} |
||||
export interface EntitiesTypes { |
||||
converters: PipelineListOption[]; |
||||
frameProcessors: PipelineListOption[]; |
||||
frameOutputs: PipelineListOption[]; |
||||
} |
||||
|
||||
export interface PipeLineEntitiesInfo { |
||||
converter: SelectableValue[]; |
||||
frameProcessors: SelectableValue[]; |
||||
frameOutputs: SelectableValue[]; |
||||
getExample: (rule: RuleType, type: string) => object; |
||||
} |
||||
|
||||
export interface ChannelFrame { |
||||
channel: string; |
||||
frame: DataFrame; |
||||
} |
@ -1,31 +0,0 @@ |
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
|
||||
import { PipelineListOption, PipeLineEntitiesInfo } from './types'; |
||||
|
||||
export async function getPipeLineEntities(): Promise<PipeLineEntitiesInfo> { |
||||
return await getBackendSrv() |
||||
.get(`api/live/pipeline-entities`) |
||||
.then((data) => { |
||||
return { |
||||
converter: transformLabel(data, 'converters'), |
||||
frameProcessors: transformLabel(data, 'frameProcessors'), |
||||
frameOutputs: transformLabel(data, 'frameOutputs'), |
||||
getExample: (ruleType, type) => { |
||||
return data[`${ruleType}s`]?.filter((option: PipelineListOption) => option.type === type)?.[0]?.['example']; |
||||
}, |
||||
}; |
||||
}); |
||||
} |
||||
|
||||
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, |
||||
})); |
||||
} |
@ -1,132 +0,0 @@ |
||||
/* Do not change, this code is generated from Golang structs */ |
||||
|
||||
import { FieldConfig } from '@grafana/data'; |
||||
|
||||
export interface ChannelAuthCheckConfig { |
||||
role?: string; |
||||
} |
||||
export interface ChannelAuthConfig { |
||||
subscribe?: ChannelAuthCheckConfig; |
||||
publish?: ChannelAuthCheckConfig; |
||||
} |
||||
export interface ChangeLogOutputConfig { |
||||
fieldName: string; |
||||
channel: string; |
||||
} |
||||
export interface RemoteWriteOutputConfig { |
||||
uid: string; |
||||
sampleMilliseconds: number; |
||||
} |
||||
export interface ThresholdOutputConfig { |
||||
fieldName: string; |
||||
channel: string; |
||||
} |
||||
export interface NumberCompareFrameConditionConfig { |
||||
fieldName: string; |
||||
op: string; |
||||
value: number; |
||||
} |
||||
export interface MultipleFrameConditionCheckerConfig { |
||||
conditionType: string; |
||||
conditions: FrameConditionCheckerConfig[]; |
||||
} |
||||
export interface FrameConditionCheckerConfig { |
||||
type: Omit<keyof FrameConditionCheckerConfig, 'type'>; |
||||
multiple?: MultipleFrameConditionCheckerConfig; |
||||
numberCompare?: NumberCompareFrameConditionConfig; |
||||
} |
||||
export interface ConditionalOutputConfig { |
||||
condition?: FrameConditionCheckerConfig; |
||||
output?: FrameOutputterConfig; |
||||
} |
||||
export interface RedirectOutputConfig { |
||||
channel: string; |
||||
} |
||||
export interface MultipleOutputterConfig { |
||||
outputs: FrameOutputterConfig[]; |
||||
} |
||||
export interface ManagedStreamOutputConfig {} |
||||
export interface FrameOutputterConfig { |
||||
type: Omit<keyof FrameOutputterConfig, 'type'>; |
||||
managedStream?: ManagedStreamOutputConfig; |
||||
multiple?: MultipleOutputterConfig; |
||||
redirect?: RedirectOutputConfig; |
||||
conditional?: ConditionalOutputConfig; |
||||
threshold?: ThresholdOutputConfig; |
||||
remoteWrite?: RemoteWriteOutputConfig; |
||||
loki?: LokiOutputConfig; |
||||
changeLog?: ChangeLogOutputConfig; |
||||
} |
||||
export interface MultipleFrameProcessorConfig { |
||||
processors: FrameProcessorConfig[]; |
||||
} |
||||
export interface KeepFieldsFrameProcessorConfig { |
||||
fieldNames: string[]; |
||||
} |
||||
export interface DropFieldsFrameProcessorConfig { |
||||
fieldNames: string[]; |
||||
} |
||||
export interface FrameProcessorConfig { |
||||
type: Omit<keyof FrameProcessorConfig, 'type'>; |
||||
dropFields?: DropFieldsFrameProcessorConfig; |
||||
keepFields?: KeepFieldsFrameProcessorConfig; |
||||
multiple?: MultipleFrameProcessorConfig; |
||||
} |
||||
export interface JsonFrameConverterConfig {} |
||||
export interface AutoInfluxConverterConfig { |
||||
frameFormat: string; |
||||
} |
||||
export interface ExactJsonConverterConfig { |
||||
fields: Field[]; |
||||
} |
||||
export interface Label { |
||||
name: string; |
||||
value: string; |
||||
} |
||||
export interface Field { |
||||
name: string; |
||||
type: number; |
||||
value: string; |
||||
labels?: Label[]; |
||||
config?: FieldConfig; |
||||
} |
||||
export interface AutoJsonConverterConfig { |
||||
fieldTips?: { [key: string]: Field }; |
||||
} |
||||
export interface ConverterConfig { |
||||
type: Omit<keyof ConverterConfig, 'type'>; |
||||
jsonAuto?: AutoJsonConverterConfig; |
||||
jsonExact?: ExactJsonConverterConfig; |
||||
influxAuto?: AutoInfluxConverterConfig; |
||||
jsonFrame?: JsonFrameConverterConfig; |
||||
} |
||||
export interface LokiOutputConfig { |
||||
uid: string; |
||||
} |
||||
export interface RedirectDataOutputConfig { |
||||
channel: string; |
||||
} |
||||
export interface DataOutputterConfig { |
||||
type: Omit<keyof DataOutputterConfig, 'type'>; |
||||
redirect?: RedirectDataOutputConfig; |
||||
loki?: LokiOutputConfig; |
||||
} |
||||
export interface MultipleSubscriberConfig { |
||||
subscribers: SubscriberConfig[]; |
||||
} |
||||
export interface SubscriberConfig { |
||||
type: Omit<keyof SubscriberConfig, 'type'>; |
||||
multiple?: MultipleSubscriberConfig; |
||||
} |
||||
export interface ChannelRuleSettings { |
||||
auth?: ChannelAuthConfig; |
||||
subscribers?: SubscriberConfig[]; |
||||
dataOutputs?: DataOutputterConfig[]; |
||||
converter?: ConverterConfig; |
||||
frameProcessors?: FrameProcessorConfig[]; |
||||
frameOutputs?: FrameOutputterConfig[]; |
||||
} |
||||
export interface ChannelRule { |
||||
pattern: string; |
||||
settings: ChannelRuleSettings; |
||||
} |
Loading…
Reference in new issue