mirror of https://github.com/grafana/grafana
Loki: Use single string expr as a state for the visual editor (#47566)
* Loki: Use expr as state for visual editor * Loki: Use query with line filter as default for visual editor * Refactor based on feedback * fix background for query text row Co-authored-by: Torkel Ödegaard <torkel@grafana.com>pull/47759/head
parent
4c99e681b1
commit
5df05e31bb
@ -0,0 +1,39 @@ |
||||
import React from 'react'; |
||||
import { render, screen } from '@testing-library/react'; |
||||
import { LokiQueryBuilderContainer } from './LokiQueryBuilderContainer'; |
||||
import { LokiDatasource } from '../../datasource'; |
||||
import { addOperation } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils'; |
||||
|
||||
describe('LokiQueryBuilderContainer', () => { |
||||
it('translates query between string and model', async () => { |
||||
const props = { |
||||
query: { |
||||
expr: '{job="testjob"}', |
||||
refId: 'A', |
||||
}, |
||||
datasource: new LokiDatasource( |
||||
{ |
||||
id: 1, |
||||
uid: '', |
||||
type: 'loki', |
||||
name: 'loki-test', |
||||
access: 'proxy', |
||||
url: '', |
||||
jsonData: {}, |
||||
meta: {} as any, |
||||
}, |
||||
undefined, |
||||
undefined |
||||
), |
||||
onChange: jest.fn(), |
||||
onRunQuery: () => {}, |
||||
}; |
||||
render(<LokiQueryBuilderContainer {...props} />); |
||||
expect(screen.getByText('testjob')).toBeInTheDocument(); |
||||
addOperation('Range functions', 'Rate'); |
||||
expect(props.onChange).toBeCalledWith({ |
||||
expr: 'rate({job="testjob"} [$__interval])', |
||||
refId: 'A', |
||||
}); |
||||
}); |
||||
}); |
@ -0,0 +1,82 @@ |
||||
import React, { useEffect, useReducer } from 'react'; |
||||
import { LokiDatasource } from '../../datasource'; |
||||
import { LokiQuery } from '../../types'; |
||||
import { buildVisualQueryFromString } from '../parsing'; |
||||
import { lokiQueryModeller } from '../LokiQueryModeller'; |
||||
import { LokiQueryBuilder } from './LokiQueryBuilder'; |
||||
import { QueryPreview } from './QueryPreview'; |
||||
import { LokiVisualQuery } from '../types'; |
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; |
||||
|
||||
export interface Props { |
||||
query: LokiQuery; |
||||
datasource: LokiDatasource; |
||||
onChange: (update: LokiQuery) => void; |
||||
onRunQuery: () => void; |
||||
} |
||||
|
||||
export interface State { |
||||
visQuery?: LokiVisualQuery; |
||||
expr: string; |
||||
} |
||||
|
||||
/** |
||||
* This component is here just to contain the translation logic between string query and the visual query builder model. |
||||
*/ |
||||
export function LokiQueryBuilderContainer(props: Props) { |
||||
const { query, onChange, onRunQuery, datasource } = props; |
||||
const [state, dispatch] = useReducer(stateSlice.reducer, { |
||||
expr: '', |
||||
visQuery: { |
||||
labels: [], |
||||
operations: [{ id: '__line_contains', params: [''] }], |
||||
}, |
||||
}); |
||||
|
||||
// Only rebuild visual query if expr changes from outside
|
||||
useEffect(() => { |
||||
dispatch(exprChanged(query.expr)); |
||||
}, [query.expr]); |
||||
|
||||
const onVisQueryChange = (visQuery: LokiVisualQuery) => { |
||||
const expr = lokiQueryModeller.renderQuery(visQuery); |
||||
dispatch(visualQueryChange({ visQuery, expr })); |
||||
onChange({ ...props.query, expr: expr }); |
||||
}; |
||||
|
||||
if (!state.visQuery) { |
||||
return null; |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
<LokiQueryBuilder |
||||
query={state.visQuery} |
||||
datasource={datasource} |
||||
onChange={onVisQueryChange} |
||||
onRunQuery={onRunQuery} |
||||
/> |
||||
<QueryPreview query={query.expr} /> |
||||
</> |
||||
); |
||||
} |
||||
|
||||
const stateSlice = createSlice({ |
||||
name: 'prom-builder-container', |
||||
initialState: { expr: '' } as State, |
||||
reducers: { |
||||
visualQueryChange: (state, action: PayloadAction<{ visQuery: LokiVisualQuery; expr: string }>) => { |
||||
state.expr = action.payload.expr; |
||||
state.visQuery = action.payload.visQuery; |
||||
}, |
||||
exprChanged: (state, action: PayloadAction<string>) => { |
||||
if (!state.visQuery || state.expr !== action.payload) { |
||||
state.expr = action.payload; |
||||
const parseResult = buildVisualQueryFromString(action.payload); |
||||
state.visQuery = parseResult.query; |
||||
} |
||||
}, |
||||
}, |
||||
}); |
||||
|
||||
const { visualQueryChange, exprChanged } = stateSlice.actions; |
Loading…
Reference in new issue