diff --git a/.betterer.results b/.betterer.results index 9dd7c3cd043..7c76001a715 100644 --- a/.betterer.results +++ b/.betterer.results @@ -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"], diff --git a/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx b/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx index 386639bab09..59ba93d3a43 100644 --- a/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/PromSettings.tsx @@ -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) => { /> +
+ 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.`} + /> +
+ 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(); +} + function renderWithProps(overrides?: Partial, componentProps: Partial = {}) { const query = defaultsDeep(overrides ?? {}, cloneDeep(defaultQuery)); const onChange = jest.fn(); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.tsx index e943754df7a..768621924c0 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryEditorSelector.tsx @@ -34,12 +34,18 @@ export const INTERVAL_FACTOR_OPTIONS: Array> = map([1, 2 type Props = PromQueryEditorProps; export const PromQueryEditorSelector = React.memo((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!; diff --git a/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts b/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts index fb8ded74555..952c55929e7 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts @@ -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', + }); + }); }); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/state.ts b/public/app/plugins/datasource/prometheus/querybuilder/state.ts index 748b8fb814f..0ffbc346b85 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/state.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/state.ts @@ -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) { diff --git a/public/app/plugins/datasource/prometheus/types.ts b/public/app/plugins/datasource/prometheus/types.ts index 5181a8fe6eb..3fbede5a34c 100644 --- a/public/app/plugins/datasource/prometheus/types.ts +++ b/public/app/plugins/datasource/prometheus/types.ts @@ -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; }