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
8 changed files with 114 additions and 15 deletions
+1 -2
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"],
@@ -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
@@ -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;
@@ -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();
@@ -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!;
@@ -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',
});
});
});
@@ -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) {
@@ -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;
}