Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prometheus: Add default editor configuration #61510

Merged
merged 2 commits into from Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions .betterer.results
Expand Up @@ -6356,8 +6356,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"],
Expand Down
Expand Up @@ -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';
Expand All @@ -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 = [
Expand Down Expand Up @@ -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';
}
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions public/app/plugins/datasource/prometheus/datasource.tsx
Expand Up @@ -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 {
ExemplarTraceIdDestination,
Expand Down Expand Up @@ -91,6 +92,7 @@ export class PrometheusDatasource
customQueryParameters: any;
datasourceConfigurationPrometheusFlavor?: PromApplication;
datasourceConfigurationPrometheusVersion?: string;
defaultEditor?: QueryEditorMode;
exemplarsAvailable: boolean;
subType: PromApplication;
rulerEnabled: boolean;
Expand Down Expand Up @@ -125,6 +127,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;

Expand Down
Expand Up @@ -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';
Expand Down Expand Up @@ -48,23 +48,48 @@ 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: '',
type: 'prometheus',
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: () => {},
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
Expand Up @@ -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!;

Expand Down
Expand Up @@ -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',
});
});
});
12 changes: 8 additions & 4 deletions public/app/plugins/datasource/prometheus/querybuilder/state.ts
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions public/app/plugins/datasource/prometheus/types.ts
Expand Up @@ -35,6 +35,7 @@ export interface PromOptions extends DataSourceJsonData {
prometheusType?: PromApplication;
prometheusVersion?: string;
enableSecureSocksProxy?: boolean;
defaultEditor?: QueryEditorMode;
}

export type ExemplarTraceIdDestination = {
Expand Down Expand Up @@ -116,6 +117,7 @@ export type PromValue = [number, any];

export interface PromMetric {
__name__?: string;

[index: string]: any;
}

Expand Down