Testdata: show settings for current simulation (#48017)

Co-authored-by: An Le <an.le@grafana.com>
This commit is contained in:
Ryan McKinley
2022-04-26 13:45:42 -07:00
committed by GitHub
parent 0da50294fe
commit 5294a2af48
7 changed files with 116 additions and 41 deletions

View File

@@ -224,7 +224,6 @@ func (s *SimulationEngine) GetSimulationHandler(rw http.ResponseWriter, req *htt
} }
result = v result = v
} else if strings.HasPrefix(path, "/sim/") { } else if strings.HasPrefix(path, "/sim/") {
rw.WriteHeader(400)
sim, err := s.getSimFromPath(path) sim, err := s.getSimFromPath(path)
if err != nil { if err != nil {
http.Error(rw, err.Error(), http.StatusNotFound) http.Error(rw, err.Error(), http.StatusNotFound)

View File

@@ -98,11 +98,11 @@ func newFlightSimInfo() simulationInfo {
df.Fields = append(df.Fields, f) df.Fields = append(df.Fields, f)
return simulationInfo{ return simulationInfo{
Type: "flight", Type: "flight",
Name: "Flight", Name: "Flight",
Description: "simple circling airplain", Description: "simple circling airplain",
SetupFields: df, ConfigFields: df,
OnlyForward: false, OnlyForward: false,
create: func(cfg simulationState) (Simulation, error) { create: func(cfg simulationState) (Simulation, error) {
s := &flightSim{ s := &flightSim{
key: cfg.Key, key: cfg.Key,

View File

@@ -134,11 +134,11 @@ func newTankSimInfo() simulationInfo {
df.Fields = append(df.Fields, data.NewField("pumpOn", nil, []bool{tc.PumpOn})) df.Fields = append(df.Fields, data.NewField("pumpOn", nil, []bool{tc.PumpOn}))
return simulationInfo{ return simulationInfo{
Type: "tank", Type: "tank",
Name: "Tank", Name: "Tank",
Description: "Fill and drain a water tank", Description: "Fill and drain a water tank",
SetupFields: df, ConfigFields: df,
OnlyForward: false, OnlyForward: false,
create: func(cfg simulationState) (Simulation, error) { create: func(cfg simulationState) (Simulation, error) {
s := &tankSim{ s := &tankSim{
key: cfg.Key, key: cfg.Key,

View File

@@ -32,11 +32,11 @@ type simulationState struct {
} }
type simulationInfo struct { type simulationInfo struct {
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
OnlyForward bool `json:"forward"` OnlyForward bool `json:"forward"`
SetupFields *data.Frame `json:"setup"` // the default setup fields (and types) ConfigFields *data.Frame `json:"config"`
// Create a simulation instance // Create a simulation instance
create func(q simulationState) (Simulation, error) create func(q simulationState) (Simulation, error)

View File

@@ -93,11 +93,11 @@ func newSinewaveInfo() simulationInfo {
df.Fields = append(df.Fields, f) df.Fields = append(df.Fields, f)
return simulationInfo{ return simulationInfo{
Type: "sine", Type: "sine",
Name: "Sine", Name: "Sine",
Description: "Sinewave generator", Description: "Sinewave generator",
SetupFields: df, ConfigFields: df,
OnlyForward: false, OnlyForward: false,
create: func(cfg simulationState) (Simulation, error) { create: func(cfg simulationState) (Simulation, error) {
s := &waveformSim{ s := &waveformSim{
key: cfg.Key, key: cfg.Key,

View File

@@ -33,6 +33,7 @@ const selectors = editorSelectors.components.DataSource.TestData.QueryTab;
export interface EditorProps { export interface EditorProps {
onChange: (value: any) => void; onChange: (value: any) => void;
query: TestDataQuery; query: TestDataQuery;
ds: TestDataDataSource;
} }
export type Props = QueryEditorProps<TestDataDataSource, TestDataQuery>; export type Props = QueryEditorProps<TestDataDataSource, TestDataQuery>;
@@ -243,13 +244,15 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
)} )}
</InlineFieldRow> </InlineFieldRow>
{scenarioId === 'random_walk' && <RandomWalkEditor onChange={onInputChange} query={query} />} {scenarioId === 'random_walk' && <RandomWalkEditor onChange={onInputChange} query={query} ds={datasource} />}
{scenarioId === 'streaming_client' && <StreamingClientEditor onChange={onStreamClientChange} query={query} />} {scenarioId === 'streaming_client' && (
{scenarioId === 'live' && <GrafanaLiveEditor onChange={onUpdate} query={query} />} <StreamingClientEditor onChange={onStreamClientChange} query={query} ds={datasource} />
{scenarioId === 'simulation' && <SimulationQueryEditor onChange={onUpdate} query={query} />} )}
{scenarioId === 'raw_frame' && <RawFrameEditor onChange={onUpdate} query={query} />} {scenarioId === 'live' && <GrafanaLiveEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'csv_file' && <CSVFileEditor onChange={onUpdate} query={query} />} {scenarioId === 'simulation' && <SimulationQueryEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'csv_content' && <CSVContentEditor onChange={onUpdate} query={query} />} {scenarioId === 'raw_frame' && <RawFrameEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'csv_file' && <CSVFileEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'csv_content' && <CSVContentEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'logs' && ( {scenarioId === 'logs' && (
<InlineFieldRow> <InlineFieldRow>
<InlineField label="Lines" labelWidth={14}> <InlineField label="Lines" labelWidth={14}>
@@ -293,12 +296,14 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
</InlineField> </InlineField>
)} )}
{scenarioId === 'predictable_pulse' && <PredictablePulseEditor onChange={onPulseWaveChange} query={query} />} {scenarioId === 'predictable_pulse' && (
<PredictablePulseEditor onChange={onPulseWaveChange} query={query} ds={datasource} />
)}
{scenarioId === 'predictable_csv_wave' && <CSVWavesEditor onChange={onCSVWaveChange} waves={query.csvWave} />} {scenarioId === 'predictable_csv_wave' && <CSVWavesEditor onChange={onCSVWaveChange} waves={query.csvWave} />}
{scenarioId === 'node_graph' && ( {scenarioId === 'node_graph' && (
<NodeGraphEditor onChange={(val: NodesQuery) => onChange({ ...query, nodes: val })} query={query} /> <NodeGraphEditor onChange={(val: NodesQuery) => onChange({ ...query, nodes: val })} query={query} />
)} )}
{scenarioId === 'server_error_500' && <ErrorEditor onChange={onUpdate} query={query} />} {scenarioId === 'server_error_500' && <ErrorEditor onChange={onUpdate} query={query} ds={datasource} />}
{description && <p>{description}</p>} {description && <p>{description}</p>}
</> </>

View File

@@ -1,19 +1,70 @@
import React, { FormEvent } from 'react'; import React, { FormEvent, useMemo } from 'react';
import { useAsync } from 'react-use';
import { SelectableValue } from '@grafana/data'; import { DataFrameJSON, SelectableValue } from '@grafana/data';
import { InlineField, InlineFieldRow, InlineSwitch, Input, Label, Select } from '@grafana/ui'; import {
InlineField,
InlineFieldRow,
Button,
FieldSet,
InlineSwitch,
Input,
Label,
Select,
Form,
TextArea,
} from '@grafana/ui';
import { EditorProps } from '../QueryEditor'; import { EditorProps } from '../QueryEditor';
import { SimulationQuery } from '../types'; import { SimulationQuery } from '../types';
export const SimulationQueryEditor = ({ onChange, query }: EditorProps) => { // Type string `json:"type"`
// Name string `json:"name"`
// Description string `json:"description"`
// OnlyForward bool `json:"forward"`
// ConfigFields *data.Frame `json:"config"`
interface SimInfo {
type: string;
name: string;
description: string;
forward: boolean;
config: DataFrameJSON;
}
interface FormDTO {
config: string;
}
export const SimulationQueryEditor = ({ onChange, query, ds }: EditorProps) => {
const simQuery = query.sim ?? ({} as SimulationQuery); const simQuery = query.sim ?? ({} as SimulationQuery);
const simKey = simQuery.key ?? ({} as typeof simQuery.key); const simKey = simQuery.key ?? ({} as typeof simQuery.key);
const options = [
{ label: 'Flight', value: 'flight' }, // This only changes once
{ label: 'Sine', value: 'sine' }, const info = useAsync(async () => {
{ label: 'Tank', value: 'tank' }, const v = (await ds.getResource('sims')) as SimInfo[];
]; return {
sims: v,
options: v.map((s) => ({ label: s.name, value: s.type, description: s.description })),
};
}, [ds]);
const current = useMemo(() => {
const type = simKey.type;
if (!type || !info.value) {
return {};
}
return {
details: info.value.sims.find((v) => v.type === type),
option: info.value.options.find((v) => v.value === type),
};
}, [info.value, simKey?.type]);
let config = useAsync(async () => {
let path = simKey.type + '/' + simKey.tick + 'hz';
if (simKey.uid) {
path += '/' + simKey.uid;
}
return (await ds.getResource('sim/' + path))?.config;
}, [simKey.type, simKey.tick, simKey.uid]);
const onUpdateKey = (key: typeof simQuery.key) => { const onUpdateKey = (key: typeof simQuery.key) => {
onChange({ ...query, sim: { ...simQuery, key } }); onChange({ ...query, sim: { ...simQuery, key } });
@@ -40,15 +91,23 @@ export const SimulationQueryEditor = ({ onChange, query }: EditorProps) => {
const onToggleLast = () => { const onToggleLast = () => {
onChange({ ...query, sim: { ...simQuery, last: !simQuery.last } }); onChange({ ...query, sim: { ...simQuery, last: !simQuery.last } });
}; };
const onSubmitChange = (data: FormDTO) => {
let path = simKey.type + '/' + simKey.tick + 'hz';
if (simKey.uid) {
path += '/' + simKey.uid;
}
ds.postResource('sim/' + path, JSON.parse(data.config));
};
return ( return (
<> <>
<InlineFieldRow> <InlineFieldRow>
<InlineField labelWidth={14} label="Simulation" tooltip=""> <InlineField labelWidth={14} label="Simulation" tooltip="">
<Select <Select
isLoading={info.loading}
menuShouldPortal menuShouldPortal
options={options} options={info.value?.options ?? []}
value={options.find((item) => item.value === simQuery.key?.type)} value={current.option}
onChange={onTypeChange} onChange={onTypeChange}
width={32} width={32}
/> />
@@ -81,6 +140,18 @@ export const SimulationQueryEditor = ({ onChange, query }: EditorProps) => {
<Input type="text" placeholder="optional" value={simQuery.key.uid} onChange={onUIDChanged} /> <Input type="text" placeholder="optional" value={simQuery.key.uid} onChange={onUIDChanged} />
</InlineField> </InlineField>
</InlineFieldRow> </InlineFieldRow>
<div>
<Form onSubmit={onSubmitChange}>
{({ register }) => (
<FieldSet>
<TextArea {...register('config')} defaultValue={JSON.stringify(config.value, null, 2)} rows={7} />
<Button type="submit">Submit</Button>
</FieldSet>
)}
</Form>
SCHEMA:
<pre>{JSON.stringify(current.details?.config.schema, null, 2)}</pre>
</div>
</> </>
); );
}; };