Prometheus: Add default editor configuration (#61510)

* Add default editor option in Prometheus configuration

* Small readability improvement
This commit is contained in:
ismail simsek 2023-01-17 15:39:15 +01:00 committed by GitHub
parent 24ef778f4d
commit 6dcc94ecb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 15 deletions

View File

@ -6320,8 +6320,7 @@ exports[`better eslint`] = {
],
"public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.test.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],

View File

@ -21,6 +21,7 @@ import {
import { useUpdateDatasource } from '../../../../features/datasources/state';
import { PromApplication, PromBuildInfoResponse } from '../../../../types/unified-alerting-dto';
import { QueryEditorMode } from '../querybuilder/shared/types';
import { PromOptions } from '../types';
import { ExemplarsSettings } from './ExemplarsSettings';
@ -33,6 +34,11 @@ const httpOptions = [
{ value: 'GET', label: 'GET' },
];
const editorOptions = [
{ value: QueryEditorMode.Builder, label: 'Builder' },
{ value: QueryEditorMode.Code, label: 'Code' },
];
type PrometheusSelectItemsType = Array<{ value: PromApplication; label: PromApplication }>;
const prometheusFlavorSelectItems: PrometheusSelectItemsType = [
@ -136,7 +142,8 @@ export const PromSettings = (props: Props) => {
// This update call is typed as void, but it returns a response which we need
const onUpdate = useUpdateDatasource();
// We are explicitly adding httpMethod so it is correctly displayed in dropdown. This way, it is more predictable for users.
// We are explicitly adding httpMethod so, it is correctly displayed in dropdown.
// This way, it is more predictable for users.
if (!options.jsonData.httpMethod) {
options.jsonData.httpMethod = 'POST';
}
@ -292,6 +299,23 @@ export const PromSettings = (props: Props) => {
/>
</InlineField>
</div>
<div className="gf-form">
<FormField
label="Default Editor"
labelWidth={14}
inputEl={
<Select
aria-label={`Default Editor (Code or Builder)`}
options={editorOptions}
value={editorOptions.find((o) => o.value === options.jsonData.defaultEditor)}
onChange={onChangeHandler('defaultEditor', options, onOptionsChange)}
width={20}
disabled={options.readOnly}
/>
}
tooltip={`Set default editor option (builder/code) for all users of this datasource. If no option was selected, the default editor will be the "builder". If they switch to other option rather than the specified with this setting on the panel we always show the selected editor for that user.`}
/>
</div>
<div className="gf-form-inline">
<div className="gf-form max-width-30">
<FormField

View File

@ -50,6 +50,7 @@ import { expandRecordingRules } from './language_utils';
import { renderLegendFormat } from './legend';
import PrometheusMetricFindQuery from './metric_find_query';
import { getInitHints, getQueryHints } from './query_hints';
import { QueryEditorMode } from './querybuilder/shared/types';
import { getOriginalMetricName, transform, transformV2 } from './result_transformer';
import { trackQuery } from './tracking';
import {
@ -92,6 +93,7 @@ export class PrometheusDatasource
customQueryParameters: any;
datasourceConfigurationPrometheusFlavor?: PromApplication;
datasourceConfigurationPrometheusVersion?: string;
defaultEditor?: QueryEditorMode;
exemplarsAvailable: boolean;
subType: PromApplication;
rulerEnabled: boolean;
@ -126,6 +128,7 @@ export class PrometheusDatasource
this.customQueryParameters = new URLSearchParams(instanceSettings.jsonData.customQueryParameters);
this.datasourceConfigurationPrometheusFlavor = instanceSettings.jsonData.prometheusType;
this.datasourceConfigurationPrometheusVersion = instanceSettings.jsonData.prometheusVersion;
this.defaultEditor = instanceSettings.jsonData.defaultEditor;
this.variables = new PrometheusVariableSupport(this, this.templateSrv, this.timeSrv);
this.exemplarsAvailable = true;

View File

@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
import { cloneDeep, defaultsDeep } from 'lodash';
import React from 'react';
import { CoreApp } from '@grafana/data';
import { CoreApp, PluginMeta, PluginType } from '@grafana/data';
import { PromQueryEditorProps } from '../../components/types';
import { PrometheusDatasource } from '../../datasource';
@ -48,8 +48,30 @@ const defaultQuery = {
expr: 'metric{label1="foo", label2="bar"}',
};
const defaultProps = {
datasource: new PrometheusDatasource(
const defaultMeta: PluginMeta = {
id: '',
name: '',
type: PluginType.datasource,
info: {
author: {
name: 'tester',
},
description: 'testing',
links: [],
logos: {
large: '',
small: '',
},
screenshots: [],
updated: '',
version: '',
},
module: '',
baseUrl: '',
};
const getDefaultDatasource = (jsonDataOverrides = {}) =>
new PrometheusDatasource(
{
id: 1,
uid: '',
@ -57,14 +79,17 @@ const defaultProps = {
name: 'prom-test',
access: 'proxy',
url: '',
jsonData: {},
meta: {} as any,
jsonData: jsonDataOverrides,
meta: defaultMeta,
readOnly: false,
},
undefined,
undefined,
new EmptyLanguageProviderMock() as unknown as PromQlLanguageProvider
),
);
const defaultProps = {
datasource: getDefaultDatasource(),
query: defaultQuery,
onRunQuery: () => {},
onChange: () => {},
@ -77,6 +102,16 @@ describe('PromQueryEditorSelector', () => {
expectCodeEditor();
});
it('shows code editor if no expr and nothing else since defaultEditor is code', async () => {
renderWithDatasourceDefaultEditorMode(QueryEditorMode.Code);
expectCodeEditor();
});
it('shows builder if no expr and nothing else since defaultEditor is builder', async () => {
renderWithDatasourceDefaultEditorMode(QueryEditorMode.Builder);
expectBuilder();
});
it('shows code editor when code mode is set', async () => {
renderWithMode(QueryEditorMode.Code);
expectCodeEditor();
@ -168,6 +203,22 @@ function renderWithMode(mode: QueryEditorMode) {
return renderWithProps({ editorMode: mode } as any);
}
function renderWithDatasourceDefaultEditorMode(mode: QueryEditorMode) {
const props = {
...defaultProps,
datasource: getDefaultDatasource({
defaultEditor: mode,
}),
query: {
refId: 'B',
expr: '',
},
onRunQuery: () => {},
onChange: () => {},
};
render(<PromQueryEditorSelector {...props} />);
}
function renderWithProps(overrides?: Partial<PromQuery>, componentProps: Partial<PromQueryEditorProps> = {}) {
const query = defaultsDeep(overrides ?? {}, cloneDeep(defaultQuery));
const onChange = jest.fn();

View File

@ -34,12 +34,18 @@ export const INTERVAL_FACTOR_OPTIONS: Array<SelectableValue<number>> = map([1, 2
type Props = PromQueryEditorProps;
export const PromQueryEditorSelector = React.memo<Props>((props) => {
const { onChange, onRunQuery, data, app } = props;
const {
onChange,
onRunQuery,
data,
app,
datasource: { defaultEditor },
} = props;
const [parseModalOpen, setParseModalOpen] = useState(false);
const [dataIsStale, setDataIsStale] = useState(false);
const { flag: explain, setFlag: setExplain } = useFlag(promQueryEditorExplainKey);
const query = getQueryWithDefaults(props.query, app);
const query = getQueryWithDefaults(props.query, app, defaultEditor);
// This should be filled in from the defaults by now.
const editorMode = query.editorMode!;

View File

@ -49,4 +49,14 @@ describe('getQueryWithDefaults(', () => {
QueryEditorMode.Code
);
});
it('should return default editor mode when it is provided', () => {
expect(getQueryWithDefaults({ refId: 'A' } as PromQuery, CoreApp.Dashboard, QueryEditorMode.Code)).toEqual({
editorMode: 'code',
expr: '',
legendFormat: '__auto',
range: true,
refId: 'A',
});
});
});

View File

@ -16,7 +16,7 @@ export function changeEditorMode(query: PromQuery, editorMode: QueryEditorMode,
onChange({ ...query, editorMode });
}
function getDefaultEditorMode(expr: string) {
function getDefaultEditorMode(expr: string, defaultEditor: QueryEditorMode = QueryEditorMode.Builder): QueryEditorMode {
// If we already have an expression default to code view
if (expr != null && expr !== '') {
return QueryEditorMode.Code;
@ -28,18 +28,22 @@ function getDefaultEditorMode(expr: string) {
case QueryEditorMode.Code:
return value;
default:
return QueryEditorMode.Builder;
return defaultEditor;
}
}
/**
* Returns query with defaults, and boolean true/false depending on change was required
*/
export function getQueryWithDefaults(query: PromQuery, app: CoreApp | undefined): PromQuery {
export function getQueryWithDefaults(
query: PromQuery,
app: CoreApp | undefined,
defaultEditor?: QueryEditorMode
): PromQuery {
let result = query;
if (!query.editorMode) {
result = { ...query, editorMode: getDefaultEditorMode(query.expr) };
result = { ...query, editorMode: getDefaultEditorMode(query.expr, defaultEditor) };
}
if (query.expr == null) {

View File

@ -35,6 +35,7 @@ export interface PromOptions extends DataSourceJsonData {
prometheusType?: PromApplication;
prometheusVersion?: string;
enableSecureSocksProxy?: boolean;
defaultEditor?: QueryEditorMode;
}
export type ExemplarTraceIdDestination = {
@ -116,6 +117,7 @@ export type PromValue = [number, any];
export interface PromMetric {
__name__?: string;
[index: string]: any;
}