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