diff --git a/pkg/tsdb/testdatasource/scenarios.go b/pkg/tsdb/testdatasource/scenarios.go index 1c4e5b4d26f..e49d760cef2 100644 --- a/pkg/tsdb/testdatasource/scenarios.go +++ b/pkg/tsdb/testdatasource/scenarios.go @@ -177,9 +177,12 @@ Timestamps will line up evenly on timeStepSeconds (For example, 60 seconds means }) s.registerScenario(&Scenario{ - ID: string(serverError500Query), - Name: "Server Error (500)", - handler: s.handleServerError500Scenario, + // Is no longer strictly a _server_ error scenario, but ID is kept for legacy :) + ID: string(serverError500Query), + Name: "Conditional Error", + handler: s.handleServerError500Scenario, + StringInput: "1,20,90,30,5,0", + Description: "Returns an error when the String Input field is empty", }) s.registerScenario(&Scenario{ @@ -449,7 +452,19 @@ func (s *Service) handlePredictablePulseScenario(ctx context.Context, req *backe } func (s *Service) handleServerError500Scenario(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { - panic("Test Data Panic!") + for _, q := range req.Queries { + model, err := simplejson.NewJson(q.JSON) + if err != nil { + continue + } + + stringInput := model.Get("stringInput").MustString() + if stringInput == "" { + panic("Test Data Panic!") + } + } + + return s.handleCSVMetricValuesScenario(ctx, req) } func (s *Service) handleClientSideScenario(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { diff --git a/public/app/plugins/datasource/testdata/QueryEditor.tsx b/public/app/plugins/datasource/testdata/QueryEditor.tsx index 1c762c22730..ae68e35ffb8 100644 --- a/public/app/plugins/datasource/testdata/QueryEditor.tsx +++ b/public/app/plugins/datasource/testdata/QueryEditor.tsx @@ -21,6 +21,7 @@ import { defaultStreamQuery } from './runStreams'; import { CSVFileEditor } from './components/CSVFileEditor'; import { CSVContentEditor } from './components/CSVContentEditor'; import { USAQueryEditor, usaQueryModes } from './components/USAQueryEditor'; +import ErrorEditor from './components/ErrorEditor'; const showLabelsFor = ['random_walk', 'predictable_pulse']; const endpoints = [ @@ -69,6 +70,7 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props) [scenarioList, query] ); const scenarioId = currentScenario?.id; + const description = currentScenario?.description; const onScenarioChange = (item: SelectableValue<string>) => { const scenario = scenarioList?.find((sc) => sc.id === item.value); @@ -287,6 +289,9 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props) {scenarioId === 'node_graph' && ( <NodeGraphEditor onChange={(val: NodesQuery) => onChange({ ...query, nodes: val })} query={query} /> )} + {scenarioId === 'server_error_500' && <ErrorEditor onChange={onUpdate} query={query} />} + + {description && <p>{description}</p>} </> ); }; diff --git a/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx b/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx new file mode 100644 index 00000000000..a8447118a88 --- /dev/null +++ b/public/app/plugins/datasource/testdata/components/ErrorEditor.tsx @@ -0,0 +1,36 @@ +import { InlineField, InlineFieldRow, Select } from '@grafana/ui'; +import React from 'react'; +import { EditorProps } from '../QueryEditor'; + +const ERROR_OPTIONS = [ + { + label: 'Server panic', + value: 'server_panic', + }, + { + label: 'Frontend exception', + value: 'frontend_exception', + }, + { + label: 'Frontend observable', + value: 'frontend_observable', + }, +]; + +const FrontendErrorQueryEditor: React.FC<EditorProps> = ({ query, onChange }) => { + return ( + <InlineFieldRow> + <InlineField labelWidth={14} label="Error type"> + <Select + options={ERROR_OPTIONS} + value={query.errorType} + onChange={(v) => { + onChange({ ...query, errorType: v.value }); + }} + /> + </InlineField> + </InlineFieldRow> + ); +}; + +export default FrontendErrorQueryEditor; diff --git a/public/app/plugins/datasource/testdata/datasource.ts b/public/app/plugins/datasource/testdata/datasource.ts index 09b8cc133b8..a92f578133f 100644 --- a/public/app/plugins/datasource/testdata/datasource.ts +++ b/public/app/plugins/datasource/testdata/datasource.ts @@ -1,4 +1,4 @@ -import { from, merge, Observable, of } from 'rxjs'; +import { from, merge, Observable, of, throwError } from 'rxjs'; import { delay } from 'rxjs/operators'; import { @@ -68,6 +68,12 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> { case 'raw_frame': streams.push(this.rawFrameQuery(target, options)); break; + case 'server_error_500': + // this now has an option where it can return/throw an error from the frontend. + // if it doesn't, send it to the backend where it might panic there :) + const query = this.serverErrorQuery(target, options); + query ? streams.push(query) : backendQueries.push(target); + break; // Unusable since 7, removed in 8 case 'manual_entry': { let csvContent = 'Time,Value\n'; @@ -202,6 +208,29 @@ export class TestDataDataSource extends DataSourceWithBackend<TestDataQuery> { return of({ data: [], error: ex }).pipe(delay(100)); } } + + serverErrorQuery( + target: TestDataQuery, + options: DataQueryRequest<TestDataQuery> + ): Observable<DataQueryResponse> | null { + const { errorType } = target; + console.log("we're here!", target); + + if (errorType === 'server_panic') { + return null; + } + + const stringInput = target.stringInput ?? ''; + if (stringInput === '') { + if (errorType === 'frontend_exception') { + throw new Error('Scenario threw an exception in the frontend because the input was empty.'); + } else { + return throwError(() => new Error('Scenario returned an error because the input was empty.')); + } + } + + return null; + } } function runGrafanaAPI(target: TestDataQuery, req: DataQueryRequest<TestDataQuery>): Observable<DataQueryResponse> { diff --git a/public/app/plugins/datasource/testdata/types.ts b/public/app/plugins/datasource/testdata/types.ts index f34442fa571..0f23a903de6 100644 --- a/public/app/plugins/datasource/testdata/types.ts +++ b/public/app/plugins/datasource/testdata/types.ts @@ -4,6 +4,7 @@ export interface Scenario { id: string; name: string; stringInput: string; + description?: string; } export interface TestDataQuery extends DataQuery { @@ -22,6 +23,7 @@ export interface TestDataQuery extends DataQuery { csvContent?: string; rawFrameContent?: string; usa?: USAQuery; + errorType?: 'server_panic' | 'frontend_exception' | 'frontend_observable'; } export interface NodesQuery {