TestData: Introduce schema types (#62130)

* schematize data query

* add the stuff you dingus

* feat(testdatasource): add scenario to generated types

* use generated testdata query in frontend

* update code owners

* Add path exception for testdata datasource

* use specific numeric data types

* fix test

* fix e2e smoketest

* add test data query type

* use test data query type

* fix betterer

* Fix typo

* move to experimental

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: sam boyer <sdboyer@grafana.com>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
Will Browne
2023-01-26 15:31:34 +00:00
committed by GitHub
co-authored by Alex Khomenko Jack Westbrook Marcus Efraimsson sam boyer
parent 0cd14091c4
commit 42732539ed
30 changed files with 876 additions and 225 deletions
+2 -1
View File
@@ -8,6 +8,7 @@ import {
SceneToolbarInput,
SceneDataNode,
} from '@grafana/scenes';
import { TestDataQueryType } from 'app/plugins/datasource/testdata/dataquery.gen';
import { panelBuilders } from '../builders/panelBuilders';
import { DashboardScene } from '../dashboard/DashboardScene';
@@ -69,7 +70,7 @@ export function getScenePanelRepeaterTest(): DashboardScene {
const queryRunner = getQueryRunnerWithRandomWalkQuery({
seriesCount: 2,
alias: '__server_names',
scenarioId: 'random_walk',
scenarioId: TestDataQueryType.RandomWalk,
});
return new DashboardScene({
@@ -1,4 +1,5 @@
import { VizPanel, SceneGridRow, SceneTimePicker, SceneGridLayout, SceneTimeRange } from '@grafana/scenes';
import { TestDataQueryType } from 'app/plugins/datasource/testdata/dataquery.gen';
import { DashboardScene } from '../dashboard/DashboardScene';
import { SceneEditManager } from '../editor/SceneEditManager';
@@ -18,7 +19,7 @@ export function getGridWithMultipleTimeRanges(): DashboardScene {
children: [
new SceneGridRow({
$timeRange: row1TimeRange,
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: 'random_walk_table' }),
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: TestDataQueryType.RandomWalkTable }),
title: 'Row A - has its own query, last year time range',
key: 'Row A',
isCollapsed: true,
@@ -1,4 +1,5 @@
import { VizPanel, SceneGridRow, SceneTimePicker, SceneGridLayout, SceneTimeRange } from '@grafana/scenes';
import { TestDataQueryType } from 'app/plugins/datasource/testdata/dataquery.gen';
import { DashboardScene } from '../dashboard/DashboardScene';
import { SceneEditManager } from '../editor/SceneEditManager';
@@ -12,7 +13,7 @@ export function getGridWithMultipleData(): DashboardScene {
children: [
new SceneGridRow({
$timeRange: new SceneTimeRange(),
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: 'random_walk_table' }),
$data: getQueryRunnerWithRandomWalkQuery({ scenarioId: TestDataQueryType.RandomWalkTable }),
title: 'Row A - has its own query',
key: 'Row A',
isCollapsed: true,
+2 -2
View File
@@ -1,8 +1,8 @@
import { QueryRunnerState, SceneQueryRunner } from '@grafana/scenes';
import { TestDataQuery } from 'app/plugins/datasource/testdata/types';
import { TestData } from 'app/plugins/datasource/testdata/dataquery.gen';
export function getQueryRunnerWithRandomWalkQuery(
overrides?: Partial<TestDataQuery>,
overrides?: Partial<TestData>,
queryRunnerOverrides?: Partial<QueryRunnerState>
) {
return new SceneQueryRunner({
+10 -4
View File
@@ -5,6 +5,7 @@ import React from 'react';
import { QueryEditor, Props } from './QueryEditor';
import { scenarios } from './__mocks__/scenarios';
import { defaultQuery } from './constants';
import { TestDataQueryType } from './dataquery.gen';
import { defaultStreamQuery } from './runStreams';
beforeEach(() => {
@@ -45,11 +46,13 @@ describe('Test Datasource Query Editor', () => {
expect(scs).toHaveLength(scenarios.length);
await userEvent.click(screen.getByText('CSV Metric Values'));
expect(mockOnChange).toHaveBeenCalledWith(expect.objectContaining({ scenarioId: 'csv_metric_values' }));
expect(mockOnChange).toHaveBeenCalledWith(
expect.objectContaining({ scenarioId: TestDataQueryType.CSVMetricValues })
);
await rerender(
<QueryEditor
{...props}
query={{ ...defaultQuery, scenarioId: 'csv_metric_values', stringInput: '1,20,90,30,5,0' }}
query={{ ...defaultQuery, scenarioId: TestDataQueryType.CSVMetricValues, stringInput: '1,20,90,30,5,0' }}
/>
);
expect(await screen.findByRole('textbox', { name: /string input/i })).toBeInTheDocument();
@@ -61,7 +64,10 @@ describe('Test Datasource Query Editor', () => {
expect.objectContaining({ scenarioId: 'grafana_api', stringInput: 'datasources' })
);
rerender(
<QueryEditor {...props} query={{ ...defaultQuery, scenarioId: 'grafana_api', stringInput: 'datasources' }} />
<QueryEditor
{...props}
query={{ ...defaultQuery, scenarioId: TestDataQueryType.GrafanaAPI, stringInput: 'datasources' }}
/>
);
expect(await screen.findByText('Grafana API')).toBeInTheDocument();
expect(screen.getByText('Data Sources')).toBeInTheDocument();
@@ -72,7 +78,7 @@ describe('Test Datasource Query Editor', () => {
expect.objectContaining({ scenarioId: 'streaming_client', stream: defaultStreamQuery })
);
const streamQuery = { ...defaultQuery, stream: defaultStreamQuery, scenarioId: 'streaming_client' };
const streamQuery = { ...defaultQuery, stream: defaultStreamQuery, scenarioId: TestDataQueryType.StreamingClient };
rerender(<QueryEditor {...props} query={streamQuery} />);
+44 -32
View File
@@ -17,9 +17,9 @@ import { RawFrameEditor } from './components/RawFrameEditor';
import { SimulationQueryEditor } from './components/SimulationQueryEditor';
import { USAQueryEditor, usaQueryModes } from './components/USAQueryEditor';
import { defaultCSVWaveQuery, defaultPulseQuery, defaultQuery } from './constants';
import { CSVWave, NodesQuery, TestData, TestDataQueryType, USAQuery } from './dataquery.gen';
import { TestDataDataSource } from './datasource';
import { defaultStreamQuery } from './runStreams';
import { CSVWave, NodesQuery, TestDataQuery, USAQuery } from './types';
const showLabelsFor = ['random_walk', 'predictable_pulse'];
const endpoints = [
@@ -32,26 +32,26 @@ const selectors = editorSelectors.components.DataSource.TestData.QueryTab;
export interface EditorProps {
onChange: (value: any) => void;
query: TestDataQuery;
query: TestData;
ds: TestDataDataSource;
}
export type Props = QueryEditorProps<TestDataDataSource, TestDataQuery>;
export type Props = QueryEditorProps<TestDataDataSource, TestData>;
export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props) => {
query = { ...defaultQuery, ...query };
const { loading, value: scenarioList } = useAsync(async () => {
// migrate manual_entry (unusable since 7, removed in 8)
if (query.scenarioId === 'manual_entry' && (query as any).points) {
if (query.scenarioId === TestDataQueryType.ManualEntry && query.points) {
let csvContent = 'Time,Value\n';
for (const point of (query as any).points) {
for (const point of query.points) {
csvContent += `${point[1]},${point[0]}\n`;
}
onChange({
refId: query.refId,
datasource: query.datasource,
scenarioId: 'csv_content',
scenarioId: TestDataQueryType.CSVContent,
csvContent,
});
}
@@ -64,7 +64,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
}));
}, []);
const onUpdate = (query: TestDataQuery) => {
const onUpdate = (query: TestData) => {
onChange(query);
onRunQuery();
};
@@ -84,8 +84,8 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
}
// Clear model from existing props that belong to other scenarios
const update: TestDataQuery = {
scenarioId: item.value!,
const update: TestData = {
scenarioId: item.value! as TestDataQueryType,
refId: query.refId,
alias: query.alias,
datasource: query.datasource,
@@ -96,25 +96,25 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
}
switch (scenario.id) {
case 'grafana_api':
case TestDataQueryType.GrafanaAPI:
update.stringInput = 'datasources';
break;
case 'streaming_client':
case TestDataQueryType.StreamingClient:
update.stream = defaultStreamQuery;
break;
case 'live':
case TestDataQueryType.Live:
update.channel = 'random-2s-stream'; // default stream
break;
case 'simulation':
case TestDataQueryType.Simulation:
update.sim = { key: { type: 'flight', tick: 10 } }; // default stream
break;
case 'predictable_pulse':
case TestDataQueryType.PredictablePulse:
update.pulseWave = defaultPulseQuery;
break;
case 'predictable_csv_wave':
case TestDataQueryType.PredictableCSVWave:
update.csvWave = defaultCSVWaveQuery;
break;
case 'usa':
case TestDataQueryType.USA:
update.usa = {
mode: usaQueryModes[0].value,
};
@@ -243,16 +243,24 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
)}
</InlineFieldRow>
{scenarioId === 'random_walk' && <RandomWalkEditor onChange={onInputChange} query={query} ds={datasource} />}
{scenarioId === 'streaming_client' && (
{scenarioId === TestDataQueryType.RandomWalk && (
<RandomWalkEditor onChange={onInputChange} query={query} ds={datasource} />
)}
{scenarioId === TestDataQueryType.StreamingClient && (
<StreamingClientEditor onChange={onStreamClientChange} query={query} ds={datasource} />
)}
{scenarioId === 'live' && <GrafanaLiveEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'simulation' && <SimulationQueryEditor onChange={onUpdate} query={query} ds={datasource} />}
{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 === TestDataQueryType.Live && <GrafanaLiveEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === TestDataQueryType.Simulation && (
<SimulationQueryEditor onChange={onUpdate} query={query} ds={datasource} />
)}
{scenarioId === TestDataQueryType.RawFrame && (
<RawFrameEditor onChange={onUpdate} query={query} ds={datasource} />
)}
{scenarioId === TestDataQueryType.CSVFile && <CSVFileEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === TestDataQueryType.CSVContent && (
<CSVContentEditor onChange={onUpdate} query={query} ds={datasource} />
)}
{scenarioId === TestDataQueryType.Logs && (
<InlineFieldRow>
<InlineField label="Lines" labelWidth={14}>
<Input
@@ -270,8 +278,8 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
</InlineFieldRow>
)}
{scenarioId === 'usa' && <USAQueryEditor onChange={onUSAStatsChange} query={query.usa ?? {}} />}
{scenarioId === 'grafana_api' && (
{scenarioId === TestDataQueryType.USA && <USAQueryEditor onChange={onUSAStatsChange} query={query.usa ?? {}} />}
{scenarioId === TestDataQueryType.GrafanaAPI && (
<InlineField labelWidth={14} label="Endpoint">
<Select
options={endpoints}
@@ -282,7 +290,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
</InlineField>
)}
{scenarioId === 'arrow' && (
{scenarioId === TestDataQueryType.Arrow && (
<InlineField grow>
<TextArea
name="stringInput"
@@ -294,15 +302,19 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
</InlineField>
)}
{scenarioId === 'predictable_pulse' && (
{scenarioId === TestDataQueryType.PredictablePulse && (
<PredictablePulseEditor onChange={onPulseWaveChange} query={query} ds={datasource} />
)}
{scenarioId === 'predictable_csv_wave' && <CSVWavesEditor onChange={onCSVWaveChange} waves={query.csvWave} />}
{scenarioId === 'node_graph' && (
{scenarioId === TestDataQueryType.PredictableCSVWave && (
<CSVWavesEditor onChange={onCSVWaveChange} waves={query.csvWave} />
)}
{scenarioId === TestDataQueryType.NodeGraph && (
<NodeGraphEditor onChange={(val: NodesQuery) => onChange({ ...query, nodes: val })} query={query} />
)}
{scenarioId === 'server_error_500' && <ErrorEditor onChange={onUpdate} query={query} ds={datasource} />}
{scenarioId === 'trace' && (
{scenarioId === TestDataQueryType.ServerError500 && (
<ErrorEditor onChange={onUpdate} query={query} ds={datasource} />
)}
{scenarioId === TestDataQueryType.Trace && (
<InlineField labelWidth={14} label="Span count">
<Input
type="number"
+22 -20
View File
@@ -1,122 +1,124 @@
import { TestDataQueryType } from '../dataquery.gen';
export const scenarios = [
{
description: '',
id: 'annotations',
id: TestDataQueryType.Annotations,
name: 'Annotations',
stringInput: '',
},
{
description: '',
id: 'arrow',
id: TestDataQueryType.Arrow,
name: 'Load Apache Arrow Data',
stringInput: '',
},
{
description: '',
id: 'csv_metric_values',
id: TestDataQueryType.CSVMetricValues,
name: 'CSV Metric Values',
stringInput: '1,20,90,30,5,0',
},
{
description: '',
id: 'datapoints_outside_range',
id: TestDataQueryType.DataPointsOutsideRange,
name: 'Datapoints Outside Range',
stringInput: '',
},
{
description: '',
id: 'exponential_heatmap_bucket_data',
id: TestDataQueryType.ExponentialHeatmapBucketData,
name: 'Exponential heatmap bucket data',
stringInput: '',
},
{
description: '',
id: 'grafana_api',
id: TestDataQueryType.GrafanaAPI,
name: 'Grafana API',
stringInput: '',
},
{
description: '',
id: 'linear_heatmap_bucket_data',
id: TestDataQueryType.LinearHeatmapBucketData,
name: 'Linear heatmap bucket data',
stringInput: '',
},
{
description: '',
id: 'logs',
id: TestDataQueryType.Logs,
name: 'Logs',
stringInput: '',
},
{
description: '',
id: 'manual_entry',
id: TestDataQueryType.ManualEntry,
name: 'Manual Entry',
stringInput: '',
},
{
description: '',
id: 'no_data_points',
id: TestDataQueryType.NoDataPoints,
name: 'No Data Points',
stringInput: '',
},
{
description: '',
id: 'predictable_csv_wave',
id: TestDataQueryType.PredictableCSVWave,
name: 'Predictable CSV Wave',
stringInput: '',
},
{
description:
'Predictable Pulse returns a pulse wave where there is a datapoint every timeStepSeconds.\nThe wave cycles at timeStepSeconds*(onCount+offCount).\nThe cycle of the wave is based off of absolute time (from the epoch) which makes it predictable.\nTimestamps will line up evenly on timeStepSeconds (For example, 60 seconds means times will all end in :00 seconds).',
id: 'predictable_pulse',
id: TestDataQueryType.PredictablePulse,
name: 'Predictable Pulse',
stringInput: '',
},
{
description: '',
id: 'random_walk',
id: TestDataQueryType.RandomWalk,
name: 'Random Walk',
stringInput: '',
},
{
description: '',
id: 'random_walk_table',
id: TestDataQueryType.RandomWalkTable,
name: 'Random Walk Table',
stringInput: '',
},
{
description: '',
id: 'random_walk_with_error',
id: TestDataQueryType.RandomWalkWithError,
name: 'Random Walk (with error)',
stringInput: '',
},
{
description: '',
id: 'server_error_500',
id: TestDataQueryType.ServerError500,
name: 'Server Error (500)',
stringInput: '',
},
{
description: '',
id: 'slow_query',
id: TestDataQueryType.SlowQuery,
name: 'Slow Query',
stringInput: '5s',
},
{
description: '',
id: 'streaming_client',
id: TestDataQueryType.StreamingClient,
name: 'Streaming Client',
stringInput: '',
},
{
description: '',
id: 'table_static',
id: TestDataQueryType.TableStatic,
name: 'Table Static',
stringInput: '',
},
{
description: '',
id: 'flame_graph',
id: TestDataQueryType.FlameGraph,
name: 'Flame Graph',
stringInput: '',
},
@@ -3,7 +3,7 @@ import React, { PureComponent, useState } from 'react';
import { Button, InlineField, InlineFieldRow, Input } from '@grafana/ui';
import { defaultCSVWaveQuery } from '../constants';
import type { CSVWave } from '../types';
import type { CSVWave } from '../dataquery.gen';
interface WavesProps {
waves?: CSVWave[];
@@ -2,11 +2,11 @@ import React from 'react';
import { Input, InlineFieldRow, InlineField, Select } from '@grafana/ui';
import { NodesQuery, TestDataQuery } from '../types';
import { NodesQuery, TestData } from '../dataquery.gen';
export interface Props {
onChange: (value: NodesQuery) => void;
query: TestDataQuery;
query: TestData;
}
export function NodeGraphEditor({ query, onChange }: Props) {
const type = query.nodes?.type || 'random';
@@ -3,7 +3,7 @@ import React, { ChangeEvent } from 'react';
import { InlineField, InlineFieldRow, Input } from '@grafana/ui';
import { EditorProps } from '../QueryEditor';
import { PulseWaveQuery } from '../types';
import { PulseWaveQuery } from '../dataquery.gen';
const fields = [
{ label: 'Step', id: 'timeStep', placeholder: '60', tooltip: 'The number of seconds between datapoints.' },
@@ -4,7 +4,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { InlineField, InlineFieldRow, Input } from '@grafana/ui';
import { EditorProps } from '../QueryEditor';
import { TestDataQuery } from '../types';
import { TestData } from '../dataquery.gen';
const randomWalkFields = [
{ label: 'Series count', id: 'seriesCount', placeholder: '1', min: 1, step: 1 },
@@ -41,7 +41,7 @@ export const RandomWalkEditor = ({ onChange, query }: EditorProps) => {
id={`randomWalk-${id}-${query.refId}`}
min={min}
step={step}
value={(query as any)[id as keyof TestDataQuery] || placeholder}
value={(query as any)[id as keyof TestData] || placeholder}
placeholder={placeholder}
onChange={onChange}
/>
@@ -5,7 +5,7 @@ import { DataFrameJSON, SelectableValue } from '@grafana/data';
import { InlineField, InlineFieldRow, InlineSwitch, Input, Label, Select } from '@grafana/ui';
import { EditorProps } from '../QueryEditor';
import { SimulationQuery } from '../types';
import { SimulationQuery } from '../dataquery.gen';
import { SimulationSchemaForm } from './SimulationSchemaForm';
@@ -4,7 +4,7 @@ import { SelectableValue } from '@grafana/data';
import { InlineField, InlineFieldRow, Input, Select } from '@grafana/ui';
import { EditorProps } from '../QueryEditor';
import { StreamingQuery } from '../types';
import { StreamingQuery } from '../dataquery.gen';
const streamingClientFields = [
{ label: 'Speed (ms)', id: 'speed', placeholder: 'value', min: 10, step: 10 },
@@ -3,7 +3,7 @@ import React from 'react';
import { SelectableValue } from '@grafana/data';
import { InlineFieldRow, InlineField, Select, MultiSelect, Input } from '@grafana/ui';
import { USAQuery } from '../types';
import { USAQuery } from '../dataquery.gen';
export interface Props {
onChange: (value: USAQuery) => void;
+3 -3
View File
@@ -1,4 +1,4 @@
import { CSVWave, TestDataQuery } from './types';
import { CSVWave, TestData, TestDataQueryType } from './dataquery.gen';
export const defaultPulseQuery: any = {
timeStep: 60,
@@ -15,7 +15,7 @@ export const defaultCSVWaveQuery: CSVWave[] = [
},
];
export const defaultQuery: TestDataQuery = {
scenarioId: 'random_walk',
export const defaultQuery: TestData = {
scenarioId: TestDataQueryType.RandomWalk,
refId: '',
};
+117
View File
@@ -0,0 +1,117 @@
// Copyright 2023 Grafana Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package grafanaplugin
import (
"github.com/grafana/grafana/packages/grafana-schema/src/common"
"github.com/grafana/grafana/pkg/plugins/pfs"
)
// This file (with its sibling .cue files) implements pfs.GrafanaPlugin
pfs.GrafanaPlugin
composableKinds: DataQuery: {
maturity: "experimental"
lineage: {
seqs: [
{
schemas: [
{
common.DataQuery
alias?: string
scenarioId?: #TestDataQueryType | *"random_walk"
stringInput?: string
stream?: #StreamingQuery
pulseWave?: #PulseWaveQuery
sim?: #SimulationQuery
csvWave?: [...#CSVWave] //TODO can we prevent partial from being generated
labels?: string
lines?: int64
levelColumn?: bool
channel?: string
nodes?: #NodesQuery
csvFileName?: string
csvContent?: string
rawFrameContent?: string
seriesCount?: int32
usa?: #USAQuery
errorType?: "server_panic" | "frontend_exception" | "frontend_observable"
spanCount?: int32
points?: [...[...string | int64]]
#TestDataQueryType: "random_walk" | "slow_query" | "random_walk_with_error" | "random_walk_table" | "exponential_heatmap_bucket_data" | "linear_heatmap_bucket_data" | "no_data_points" | "datapoints_outside_range" | "csv_metric_values" | "predictable_pulse" | "predictable_csv_wave" | "streaming_client" | "simulation" | "usa" | "live" | "grafana_api" | "arrow" | "annotations" | "table_static" | "server_error_500" | "logs" | "node_graph" | "flame_graph" | "raw_frame" | "csv_file" | "csv_content" | "trace" | "manual_entry" | "variables-query" @cuetsy(kind="enum", memberNames="RandomWalk|SlowQuery|RandomWalkWithError|RandomWalkTable|ExponentialHeatmapBucketData|LinearHeatmapBucketData|NoDataPoints|DataPointsOutsideRange|CSVMetricValues|PredictablePulse|PredictableCSVWave|StreamingClient|Simulation|USA|Live|GrafanaAPI|Arrow|Annotations|TableStatic|ServerError500|Logs|NodeGraph|FlameGraph|RawFrame|CSVFile|CSVContent|Trace|ManualEntry|VariablesQuery")
#StreamingQuery: {
type: "signal" | "logs" | "fetch"
speed: int32
spread: int32
noise: int32
bands?: int32
url?: string
} @cuetsy(kind="interface")
#PulseWaveQuery: {
timeStep?: int64
onCount?: int64
offCount?: int64
onValue?: float64
offValue?: float64
} @cuetsy(kind="interface")
#SimulationQuery: {
key: {
type: string
tick: float64
uid?: string
}
config?: {...}
stream?: bool
last?: bool
} @cuetsy(kind="interface")
#NodesQuery: {
type?: "random" | "response" | "random edges"
count?: int64
} @cuetsy(kind="interface")
#USAQuery: {
mode?: string
period?: string
fields?: [...string]
states?: [...string]
} @cuetsy(kind="interface")
#CSVWave: {
timeStep?: int64
name?: string
valuesCSV?: string
labels?: string
} @cuetsy(kind="interface")
// TODO: Should this live here given it's not used in the dataquery?
#Scenario: {
id: string
name: string
stringInput: string
description?: string
hideAliasField?: bool
} @cuetsy(kind="interface")
},
]
},
]
}
}
+137
View File
@@ -0,0 +1,137 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
//
// Generated by:
// public/app/plugins/gen.go
// Using jennies:
// TSTypesJenny
// PluginTSTypesJenny
//
// Run 'make gen-cue' from repository root to regenerate.
import * as common from '@grafana/schema';
export const DataQueryModelVersion = Object.freeze([0, 0]);
export enum TestDataQueryType {
Annotations = 'annotations',
Arrow = 'arrow',
CSVContent = 'csv_content',
CSVFile = 'csv_file',
CSVMetricValues = 'csv_metric_values',
DataPointsOutsideRange = 'datapoints_outside_range',
ExponentialHeatmapBucketData = 'exponential_heatmap_bucket_data',
FlameGraph = 'flame_graph',
GrafanaAPI = 'grafana_api',
LinearHeatmapBucketData = 'linear_heatmap_bucket_data',
Live = 'live',
Logs = 'logs',
ManualEntry = 'manual_entry',
NoDataPoints = 'no_data_points',
NodeGraph = 'node_graph',
PredictableCSVWave = 'predictable_csv_wave',
PredictablePulse = 'predictable_pulse',
RandomWalk = 'random_walk',
RandomWalkTable = 'random_walk_table',
RandomWalkWithError = 'random_walk_with_error',
RawFrame = 'raw_frame',
ServerError500 = 'server_error_500',
Simulation = 'simulation',
SlowQuery = 'slow_query',
StreamingClient = 'streaming_client',
TableStatic = 'table_static',
Trace = 'trace',
USA = 'usa',
VariablesQuery = 'variables-query',
}
export interface StreamingQuery {
bands?: number;
noise: number;
speed: number;
spread: number;
type: ('signal' | 'logs' | 'fetch');
url?: string;
}
export interface PulseWaveQuery {
offCount?: number;
offValue?: number;
onCount?: number;
onValue?: number;
timeStep?: number;
}
export interface SimulationQuery {
config?: Record<string, unknown>;
key: {
type: string;
tick: number;
uid?: string;
};
last?: boolean;
stream?: boolean;
}
export interface NodesQuery {
count?: number;
type?: ('random' | 'response' | 'random edges');
}
export interface USAQuery {
fields?: Array<string>;
mode?: string;
period?: string;
states?: Array<string>;
}
export const defaultUSAQuery: Partial<USAQuery> = {
fields: [],
states: [],
};
export interface CSVWave {
labels?: string;
name?: string;
timeStep?: number;
valuesCSV?: string;
}
/**
* TODO: Should this live here given it's not used in the dataquery?
*/
export interface Scenario {
description?: string;
hideAliasField?: boolean;
id: string;
name: string;
stringInput: string;
}
export interface TestData extends common.DataQuery {
alias?: string;
channel?: string;
csvContent?: string;
csvFileName?: string;
csvWave?: Array<CSVWave>;
errorType?: ('server_panic' | 'frontend_exception' | 'frontend_observable');
labels?: string;
levelColumn?: boolean;
lines?: number;
nodes?: NodesQuery;
points?: Array<Array<(string | number)>>;
pulseWave?: PulseWaveQuery;
rawFrameContent?: string;
scenarioId?: TestDataQueryType;
seriesCount?: number;
sim?: SimulationQuery;
spanCount?: number;
stream?: StreamingQuery;
stringInput?: string;
usa?: USAQuery;
}
export const defaultTestData: Partial<TestData> = {
csvWave: [],
points: [],
scenarioId: TestDataQueryType.RandomWalk,
};
+18 -24
View File
@@ -19,14 +19,14 @@ import {
import { DataSourceWithBackend, getBackendSrv, getGrafanaLiveSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import { getSearchFilterScopedVar } from 'app/features/variables/utils';
import { Scenario, TestData, TestDataQueryType } from './dataquery.gen';
import { queryMetricTree } from './metricTree';
import { generateRandomEdges, generateRandomNodes, savedNodesResponse } from './nodeGraphUtils';
import { runStream } from './runStreams';
import { flameGraphData } from './testData/flameGraphResponse';
import { Scenario, TestDataQuery } from './types';
import { TestDataVariableSupport } from './variables';
export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
export class TestDataDataSource extends DataSourceWithBackend<TestData> {
scenariosCache?: Promise<Scenario[]>;
constructor(
@@ -37,8 +37,8 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
this.variables = new TestDataVariableSupport();
}
query(options: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
const backendQueries: TestDataQuery[] = [];
query(options: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const backendQueries: TestData[] = [];
const streams: Array<Observable<DataQueryResponse>> = [];
// Start streams and prepare queries
@@ -86,12 +86,12 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
// Unusable since 7, removed in 8
case 'manual_entry': {
let csvContent = 'Time,Value\n';
if ((target as any).points) {
for (const point of (target as any).points) {
if (target.points) {
for (const point of target.points) {
csvContent += `${point[1]},${point[0]}\n`;
}
}
target.scenarioId = 'csv_content';
target.scenarioId = TestDataQueryType.CSVContent;
target.csvContent = csvContent;
}
@@ -115,7 +115,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return merge(...streams);
}
resolveTemplateVariables(query: TestDataQuery, scopedVars: ScopedVars) {
resolveTemplateVariables(query: TestData, scopedVars: ScopedVars) {
if (query.labels) {
query.labels = this.templateSrv.replace(query.labels, scopedVars);
}
@@ -123,7 +123,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
query.alias = this.templateSrv.replace(query.alias, scopedVars);
}
if (query.scenarioId) {
query.scenarioId = this.templateSrv.replace(query.scenarioId, scopedVars);
query.scenarioId = this.templateSrv.replace(query.scenarioId, scopedVars) as TestDataQueryType;
}
if (query.stringInput) {
query.stringInput = this.templateSrv.replace(query.stringInput, scopedVars);
@@ -136,7 +136,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
}
}
annotationDataTopicTest(target: TestDataQuery, req: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
annotationDataTopicTest(target: TestData, req: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const events = this.buildFakeAnnotationEvents(req.range, 50);
const dataFrame = new ArrayDataFrame(events);
dataFrame.meta = { dataTopic: DataTopic.Annotations };
@@ -166,7 +166,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return Promise.resolve(this.buildFakeAnnotationEvents(options.range, 10));
}
getQueryDisplayText(query: TestDataQuery) {
getQueryDisplayText(query: TestData) {
const scenario = query.scenarioId ?? 'Default scenario';
if (query.alias) {
@@ -191,7 +191,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return this.scenariosCache;
}
variablesQuery(target: TestDataQuery, options: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
variablesQuery(target: TestData, options: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const query = target.stringInput ?? '';
const interpolatedQuery = this.templateSrv.replace(
query,
@@ -204,7 +204,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return of({ data: [dataFrame] }).pipe(delay(100));
}
nodesQuery(target: TestDataQuery, options: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
nodesQuery(target: TestData, options: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const type = target.nodes?.type || 'random';
let frames: DataFrame[];
switch (type) {
@@ -228,7 +228,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return of({ data: [flameGraphData] }).pipe(delay(100));
}
trace(target: TestDataQuery, options: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
trace(target: TestData, options: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const frame = new MutableDataFrame({
meta: {
preferredVisualisationType: 'trace',
@@ -266,7 +266,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
return of({ data: [frame] }).pipe(delay(100));
}
rawFrameQuery(target: TestDataQuery, options: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
rawFrameQuery(target: TestData, options: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
try {
const data = JSON.parse(target.rawFrameContent ?? '[]').map((v: any) => {
const f = toDataFrame(v);
@@ -282,10 +282,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
}
}
serverErrorQuery(
target: TestDataQuery,
options: DataQueryRequest<TestDataQuery>
): Observable<DataQueryResponse> | null {
serverErrorQuery(target: TestData, options: DataQueryRequest<TestData>): Observable<DataQueryResponse> | null {
const { errorType } = target;
if (errorType === 'server_panic') {
@@ -305,7 +302,7 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> {
}
}
function runGrafanaAPI(target: TestDataQuery, req: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
function runGrafanaAPI(target: TestData, req: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const url = `/api/${target.stringInput}`;
return from(
getBackendSrv()
@@ -322,10 +319,7 @@ function runGrafanaAPI(target: TestDataQuery, req: DataQueryRequest<TestDataQuer
let liveQueryCounter = 1000;
function runGrafanaLiveQuery(
target: TestDataQuery,
req: DataQueryRequest<TestDataQuery>
): Observable<DataQueryResponse> {
function runGrafanaLiveQuery(target: TestData, req: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
if (!target.channel) {
throw new Error(`Missing channel config`);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"type": "datasource",
"name": "TestData DB",
"name": "TestData",
"id": "testdata",
"metrics": true,
+8 -8
View File
@@ -16,7 +16,7 @@ import { liveTimer } from 'app/features/dashboard/dashgrid/liveTimer';
import { StreamingDataFrame } from 'app/features/live/data/StreamingDataFrame';
import { getRandomLine } from './LogIpsum';
import { TestDataQuery, StreamingQuery } from './types';
import { TestData, StreamingQuery } from './dataquery.gen';
export const defaultStreamQuery: StreamingQuery = {
type: 'signal',
@@ -26,7 +26,7 @@ export const defaultStreamQuery: StreamingQuery = {
bands: 1,
};
export function runStream(target: TestDataQuery, req: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> {
export function runStream(target: TestData, req: DataQueryRequest<TestData>): Observable<DataQueryResponse> {
const query = defaults(target.stream, defaultStreamQuery);
if ('signal' === query.type) {
return runSignalStream(target, query, req);
@@ -41,9 +41,9 @@ export function runStream(target: TestDataQuery, req: DataQueryRequest<TestDataQ
}
export function runSignalStream(
target: TestDataQuery,
target: TestData,
query: StreamingQuery,
req: DataQueryRequest<TestDataQuery>
req: DataQueryRequest<TestData>
): Observable<DataQueryResponse> {
return new Observable<DataQueryResponse>((subscriber) => {
const streamId = `signal-${req.panelId}-${target.refId}`;
@@ -128,9 +128,9 @@ export function runSignalStream(
}
export function runLogsStream(
target: TestDataQuery,
target: TestData,
query: StreamingQuery,
req: DataQueryRequest<TestDataQuery>
req: DataQueryRequest<TestData>
): Observable<DataQueryResponse> {
return new Observable<DataQueryResponse>((subscriber) => {
const streamId = `logs-${req.panelId}-${target.refId}`;
@@ -173,9 +173,9 @@ export function runLogsStream(
}
export function runFetchStream(
target: TestDataQuery,
target: TestData,
query: StreamingQuery,
req: DataQueryRequest<TestDataQuery>
req: DataQueryRequest<TestData>
): Observable<DataQueryResponse> {
return new Observable<DataQueryResponse>((subscriber) => {
const streamId = `fetch-${req.panelId}-${target.refId}`;
-77
View File
@@ -1,77 +0,0 @@
import { DataQuery } from '@grafana/data';
export interface Scenario {
id: string;
name: string;
stringInput: string;
description?: string;
hideAliasField?: boolean;
}
export interface TestDataQuery extends DataQuery {
alias?: string;
scenarioId?: string;
stringInput?: string;
stream?: StreamingQuery;
pulseWave?: PulseWaveQuery;
sim?: SimulationQuery;
csvWave?: CSVWave[];
labels?: string;
lines?: number;
levelColumn?: boolean;
channel?: string; // for grafana live
nodes?: NodesQuery;
csvFileName?: string;
csvContent?: string;
rawFrameContent?: string;
seriesCount?: number;
usa?: USAQuery;
errorType?: 'server_panic' | 'frontend_exception' | 'frontend_observable';
spanCount?: number;
}
export interface NodesQuery {
type?: 'random' | 'response' | 'random edges';
count?: number;
}
export interface StreamingQuery {
type: 'signal' | 'logs' | 'fetch';
speed: number;
spread: number;
noise: number; // wiggle around the signal for min/max
bands?: number; // number of bands around the middle band
url?: string; // the Fetch URL
}
export interface SimulationQuery {
key: {
type: string;
tick: number;
uid?: string;
};
config?: Record<string, any>;
stream?: boolean;
last?: boolean;
}
export interface PulseWaveQuery {
timeStep?: number;
onCount?: number;
offCount?: number;
onValue?: number;
offValue?: number;
}
export interface CSVWave {
timeStep?: number;
name?: string;
valuesCSV?: string;
labels?: string;
}
export interface USAQuery {
mode?: string;
period?: string;
fields?: string[]; // foo, bar, baz
states?: string[];
}
+3 -3
View File
@@ -1,14 +1,14 @@
import { StandardVariableQuery, StandardVariableSupport } from '@grafana/data';
import { TestData, TestDataQueryType } from './dataquery.gen';
import { TestDataDataSource } from './datasource';
import { TestDataQuery } from './types';
export class TestDataVariableSupport extends StandardVariableSupport<TestDataDataSource> {
toDataQuery(query: StandardVariableQuery): TestDataQuery {
toDataQuery(query: StandardVariableQuery): TestData {
return {
refId: 'TestDataDataSource-QueryVariable',
stringInput: query.query,
scenarioId: 'variables-query',
scenarioId: TestDataQueryType.VariablesQuery,
csvWave: undefined,
};
}