diff --git a/pkg/tsdb/testdatasource/sims/engine.go b/pkg/tsdb/testdatasource/sims/engine.go index fa18df54bc6..32d97cac668 100644 --- a/pkg/tsdb/testdatasource/sims/engine.go +++ b/pkg/tsdb/testdatasource/sims/engine.go @@ -224,7 +224,6 @@ func (s *SimulationEngine) GetSimulationHandler(rw http.ResponseWriter, req *htt } result = v } else if strings.HasPrefix(path, "/sim/") { - rw.WriteHeader(400) sim, err := s.getSimFromPath(path) if err != nil { http.Error(rw, err.Error(), http.StatusNotFound) diff --git a/pkg/tsdb/testdatasource/sims/flight_path.go b/pkg/tsdb/testdatasource/sims/flight_path.go index 0bc00fc5395..b089a336934 100644 --- a/pkg/tsdb/testdatasource/sims/flight_path.go +++ b/pkg/tsdb/testdatasource/sims/flight_path.go @@ -98,11 +98,11 @@ func newFlightSimInfo() simulationInfo { df.Fields = append(df.Fields, f) return simulationInfo{ - Type: "flight", - Name: "Flight", - Description: "simple circling airplain", - SetupFields: df, - OnlyForward: false, + Type: "flight", + Name: "Flight", + Description: "simple circling airplain", + ConfigFields: df, + OnlyForward: false, create: func(cfg simulationState) (Simulation, error) { s := &flightSim{ key: cfg.Key, diff --git a/pkg/tsdb/testdatasource/sims/tank.go b/pkg/tsdb/testdatasource/sims/tank.go index ce8c1626374..fea93c08c23 100644 --- a/pkg/tsdb/testdatasource/sims/tank.go +++ b/pkg/tsdb/testdatasource/sims/tank.go @@ -134,11 +134,11 @@ func newTankSimInfo() simulationInfo { df.Fields = append(df.Fields, data.NewField("pumpOn", nil, []bool{tc.PumpOn})) return simulationInfo{ - Type: "tank", - Name: "Tank", - Description: "Fill and drain a water tank", - SetupFields: df, - OnlyForward: false, + Type: "tank", + Name: "Tank", + Description: "Fill and drain a water tank", + ConfigFields: df, + OnlyForward: false, create: func(cfg simulationState) (Simulation, error) { s := &tankSim{ key: cfg.Key, diff --git a/pkg/tsdb/testdatasource/sims/types.go b/pkg/tsdb/testdatasource/sims/types.go index 42b9e7735d7..00bd8ab7037 100644 --- a/pkg/tsdb/testdatasource/sims/types.go +++ b/pkg/tsdb/testdatasource/sims/types.go @@ -32,11 +32,11 @@ type simulationState struct { } type simulationInfo struct { - Type string `json:"type"` - Name string `json:"name"` - Description string `json:"description"` - OnlyForward bool `json:"forward"` - SetupFields *data.Frame `json:"setup"` // the default setup fields (and types) + Type string `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + OnlyForward bool `json:"forward"` + ConfigFields *data.Frame `json:"config"` // Create a simulation instance create func(q simulationState) (Simulation, error) diff --git a/pkg/tsdb/testdatasource/sims/waveform.go b/pkg/tsdb/testdatasource/sims/waveform.go index 6f78dd4b2a9..c1374ceb89e 100644 --- a/pkg/tsdb/testdatasource/sims/waveform.go +++ b/pkg/tsdb/testdatasource/sims/waveform.go @@ -93,11 +93,11 @@ func newSinewaveInfo() simulationInfo { df.Fields = append(df.Fields, f) return simulationInfo{ - Type: "sine", - Name: "Sine", - Description: "Sinewave generator", - SetupFields: df, - OnlyForward: false, + Type: "sine", + Name: "Sine", + Description: "Sinewave generator", + ConfigFields: df, + OnlyForward: false, create: func(cfg simulationState) (Simulation, error) { s := &waveformSim{ key: cfg.Key, diff --git a/public/app/plugins/datasource/testdata/QueryEditor.tsx b/public/app/plugins/datasource/testdata/QueryEditor.tsx index 38b9021ad6c..f7aaa596287 100644 --- a/public/app/plugins/datasource/testdata/QueryEditor.tsx +++ b/public/app/plugins/datasource/testdata/QueryEditor.tsx @@ -33,6 +33,7 @@ const selectors = editorSelectors.components.DataSource.TestData.QueryTab; export interface EditorProps { onChange: (value: any) => void; query: TestDataQuery; + ds: TestDataDataSource; } export type Props = QueryEditorProps; @@ -243,13 +244,15 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props) )} - {scenarioId === 'random_walk' && } - {scenarioId === 'streaming_client' && } - {scenarioId === 'live' && } - {scenarioId === 'simulation' && } - {scenarioId === 'raw_frame' && } - {scenarioId === 'csv_file' && } - {scenarioId === 'csv_content' && } + {scenarioId === 'random_walk' && } + {scenarioId === 'streaming_client' && ( + + )} + {scenarioId === 'live' && } + {scenarioId === 'simulation' && } + {scenarioId === 'raw_frame' && } + {scenarioId === 'csv_file' && } + {scenarioId === 'csv_content' && } {scenarioId === 'logs' && ( @@ -293,12 +296,14 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props) )} - {scenarioId === 'predictable_pulse' && } + {scenarioId === 'predictable_pulse' && ( + + )} {scenarioId === 'predictable_csv_wave' && } {scenarioId === 'node_graph' && ( onChange({ ...query, nodes: val })} query={query} /> )} - {scenarioId === 'server_error_500' && } + {scenarioId === 'server_error_500' && } {description &&

{description}

} diff --git a/public/app/plugins/datasource/testdata/components/SimulationQueryEditor.tsx b/public/app/plugins/datasource/testdata/components/SimulationQueryEditor.tsx index abaca3d4a84..09cafea4db2 100644 --- a/public/app/plugins/datasource/testdata/components/SimulationQueryEditor.tsx +++ b/public/app/plugins/datasource/testdata/components/SimulationQueryEditor.tsx @@ -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 { InlineField, InlineFieldRow, InlineSwitch, Input, Label, Select } from '@grafana/ui'; +import { DataFrameJSON, SelectableValue } from '@grafana/data'; +import { + InlineField, + InlineFieldRow, + Button, + FieldSet, + InlineSwitch, + Input, + Label, + Select, + Form, + TextArea, +} from '@grafana/ui'; import { EditorProps } from '../QueryEditor'; 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 simKey = simQuery.key ?? ({} as typeof simQuery.key); - const options = [ - { label: 'Flight', value: 'flight' }, - { label: 'Sine', value: 'sine' }, - { label: 'Tank', value: 'tank' }, - ]; + + // This only changes once + const info = useAsync(async () => { + 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) => { onChange({ ...query, sim: { ...simQuery, key } }); @@ -40,15 +91,23 @@ export const SimulationQueryEditor = ({ onChange, query }: EditorProps) => { const onToggleLast = () => { 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 ( <> +
+
+ {({ register }) => ( +
+