mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Remove support for legacy, compact format URLs (#49350)
* Remove function, start changing tests * Fix tests * Remove deprecated parameter from function
This commit is contained in:
parent
be0d043673
commit
a9cc3225ba
@ -196,15 +196,11 @@ export const urlUtil = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an string that is used in URL to represent the Explore state. This is basically just a stringified json
|
||||
* that is that used as a state of a single Explore pane so it does not represent full Explore URL.
|
||||
* Create an string that is used in URL to represent the Explore state. This is stringified json
|
||||
* that is used as a state of a single Explore pane - it does not represent full Explore URL.
|
||||
*
|
||||
* @param urlState
|
||||
* @param compact this parameter is deprecated and will be removed in a future release.
|
||||
*/
|
||||
export function serializeStateToUrlParam(urlState: ExploreUrlState, compact?: boolean): string {
|
||||
if (compact !== undefined) {
|
||||
console.warn('`compact` parameter is deprecated and will be removed in a future release');
|
||||
}
|
||||
export function serializeStateToUrlParam(urlState: ExploreUrlState): string {
|
||||
return JSON.stringify(urlState);
|
||||
}
|
||||
|
@ -48,45 +48,6 @@ describe('state functions', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a valid Explore state from a compact URL parameter', () => {
|
||||
const paramValue = '["now-1h","now","Local",{"expr":"metric"},{"ui":[true,true,true,"none"]}]';
|
||||
expect(parseUrlState(paramValue)).toMatchObject({
|
||||
datasource: 'Local',
|
||||
queries: [{ expr: 'metric' }],
|
||||
range: {
|
||||
from: 'now-1h',
|
||||
to: 'now',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return a query for mode in the url', () => {
|
||||
// Previous versions of Grafana included "Explore mode" in the URL; this should not be treated as a query.
|
||||
const paramValue =
|
||||
'["now-1h","now","x-ray-datasource",{"queryType":"getTraceSummaries"},{"mode":"Metrics"},{"ui":[true,true,true,"none"]}]';
|
||||
expect(parseUrlState(paramValue)).toMatchObject({
|
||||
datasource: 'x-ray-datasource',
|
||||
queries: [{ queryType: 'getTraceSummaries' }],
|
||||
range: {
|
||||
from: 'now-1h',
|
||||
to: 'now',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should return queries if queryType is present in the url', () => {
|
||||
const paramValue =
|
||||
'["now-1h","now","x-ray-datasource",{"queryType":"getTraceSummaries"},{"ui":[true,true,true,"none"]}]';
|
||||
expect(parseUrlState(paramValue)).toMatchObject({
|
||||
datasource: 'x-ray-datasource',
|
||||
queries: [{ queryType: 'getTraceSummaries' }],
|
||||
range: {
|
||||
from: 'now-1h',
|
||||
to: 'now',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('serializeStateToUrlParam', () => {
|
||||
|
@ -165,16 +165,6 @@ export function buildQueryTransaction(
|
||||
|
||||
export const clearQueryKeys: (query: DataQuery) => DataQuery = ({ key, ...rest }) => rest;
|
||||
|
||||
const isSegment = (segment: { [key: string]: string }, ...props: string[]) =>
|
||||
props.some((prop) => segment.hasOwnProperty(prop));
|
||||
|
||||
enum ParseUrlStateIndex {
|
||||
RangeFrom = 0,
|
||||
RangeTo = 1,
|
||||
Datasource = 2,
|
||||
SegmentsStart = 3,
|
||||
}
|
||||
|
||||
export const safeParseJson = (text?: string): any | undefined => {
|
||||
if (!text) {
|
||||
return;
|
||||
@ -229,25 +219,7 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState {
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
if (!Array.isArray(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
if (parsed.length <= ParseUrlStateIndex.SegmentsStart) {
|
||||
console.error('Error parsing compact URL state for Explore.');
|
||||
return errorResult;
|
||||
}
|
||||
|
||||
const range = {
|
||||
from: parsed[ParseUrlStateIndex.RangeFrom],
|
||||
to: parsed[ParseUrlStateIndex.RangeTo],
|
||||
};
|
||||
const datasource = parsed[ParseUrlStateIndex.Datasource];
|
||||
const parsedSegments = parsed.slice(ParseUrlStateIndex.SegmentsStart);
|
||||
const queries = parsedSegments.filter((segment) => !isSegment(segment, 'ui', 'mode', '__panelsState'));
|
||||
|
||||
const panelsState = parsedSegments.find((segment) => isSegment(segment, '__panelsState'))?.__panelsState;
|
||||
return { datasource, queries, range, panelsState };
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function generateKey(index = 0): string {
|
||||
|
@ -12,6 +12,40 @@ import { splitOpen } from './state/main';
|
||||
|
||||
type Mock = jest.Mock;
|
||||
|
||||
type overrideParamsType = {
|
||||
datasource?: string;
|
||||
exprValue?: string;
|
||||
rightDatasource?: string;
|
||||
rightExprValue?: string;
|
||||
};
|
||||
|
||||
const defaultUrlParams = ({
|
||||
datasource = 'loki',
|
||||
exprValue = '{label="value"}',
|
||||
rightDatasource,
|
||||
rightExprValue,
|
||||
}: overrideParamsType) => {
|
||||
type urlParamsType = { left: string; right?: string };
|
||||
|
||||
const urlParams: urlParamsType = {
|
||||
left: serializeStateToUrlParam({
|
||||
datasource: datasource,
|
||||
queries: [{ refId: 'A', expr: exprValue }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
};
|
||||
|
||||
if (rightDatasource) {
|
||||
urlParams.right = serializeStateToUrlParam({
|
||||
datasource: rightDatasource,
|
||||
queries: [{ refId: 'A', expr: rightExprValue ? rightExprValue : exprValue }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
});
|
||||
}
|
||||
|
||||
return urlParams;
|
||||
};
|
||||
|
||||
jest.mock('app/core/core', () => {
|
||||
return {
|
||||
contextSrv: {
|
||||
@ -61,13 +95,7 @@ describe('Wrapper', () => {
|
||||
});
|
||||
|
||||
it('runs query when url contains query and renders results', async () => {
|
||||
const urlParams = {
|
||||
left: serializeStateToUrlParam({
|
||||
datasource: 'loki',
|
||||
queries: [{ refId: 'A', expr: '{ label="value"}' }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
};
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
|
||||
@ -78,7 +106,7 @@ describe('Wrapper', () => {
|
||||
await screen.findByText(/custom log line/i);
|
||||
|
||||
// And that the editor gets the expr from the url
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
|
||||
// We did not change the url
|
||||
expect(locationService.getSearchObject()).toEqual({
|
||||
@ -89,12 +117,12 @@ describe('Wrapper', () => {
|
||||
// We called the data source query method once
|
||||
expect(datasources.loki.query).toBeCalledTimes(1);
|
||||
expect((datasources.loki.query as Mock).mock.calls[0][0]).toMatchObject({
|
||||
targets: [{ expr: '{ label="value"}' }],
|
||||
targets: [{ expr: '{label="value"}' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('handles url change and runs the new query', async () => {
|
||||
const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]) };
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
// Wait for rendering the logs
|
||||
@ -102,29 +130,25 @@ describe('Wrapper', () => {
|
||||
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse('different log'));
|
||||
|
||||
locationService.partial({
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="different"}' }]),
|
||||
});
|
||||
locationService.partial(defaultUrlParams({ exprValue: '{label="different"}' }));
|
||||
|
||||
// Editor renders the new query
|
||||
await screen.findByText(`loki Editor input: { label="different"}`);
|
||||
await screen.findByText(`loki Editor input: {label="different"}`);
|
||||
// Renders new response
|
||||
await screen.findByText(/different log/i);
|
||||
});
|
||||
|
||||
it('handles url change and runs the new query with different datasource', async () => {
|
||||
const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]) };
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
// Wait for rendering the logs
|
||||
await screen.findByText(/custom log line/i);
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
|
||||
(datasources.elastic.query as Mock).mockReturnValueOnce(makeMetricsQueryResponse());
|
||||
|
||||
locationService.partial({
|
||||
left: JSON.stringify(['now-1h', 'now', 'elastic', { expr: 'other query' }]),
|
||||
});
|
||||
locationService.partial(defaultUrlParams({ datasource: 'elastic', exprValue: 'other query' }));
|
||||
|
||||
// Editor renders the new query
|
||||
await screen.findByText(`elastic Editor input: other query`);
|
||||
@ -133,7 +157,7 @@ describe('Wrapper', () => {
|
||||
});
|
||||
|
||||
it('handles changing the datasource manually', async () => {
|
||||
const urlParams = { left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}', refId: 'A' }]) };
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
await waitForExplore();
|
||||
@ -163,18 +187,7 @@ describe('Wrapper', () => {
|
||||
});
|
||||
|
||||
it('inits with two panes if specified in url', async () => {
|
||||
const urlParams = {
|
||||
left: serializeStateToUrlParam({
|
||||
datasource: 'loki',
|
||||
queries: [{ refId: 'A', expr: '{ label="value"}' }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
right: serializeStateToUrlParam({
|
||||
datasource: 'elastic',
|
||||
queries: [{ refId: 'A', expr: 'error' }],
|
||||
range: { from: 'now-1h', to: 'now' },
|
||||
}),
|
||||
};
|
||||
const urlParams = defaultUrlParams({ rightDatasource: 'elastic', rightExprValue: 'error' });
|
||||
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
@ -191,7 +204,7 @@ describe('Wrapper', () => {
|
||||
expect(logsLines.length).toBe(2);
|
||||
|
||||
// And that the editor gets the expr from the url
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
await screen.findByText(`elastic Editor input: error`);
|
||||
|
||||
// We did not change the url
|
||||
@ -203,7 +216,7 @@ describe('Wrapper', () => {
|
||||
// We called the data source query method once
|
||||
expect(datasources.loki.query).toBeCalledTimes(1);
|
||||
expect((datasources.loki.query as Mock).mock.calls[0][0]).toMatchObject({
|
||||
targets: [{ expr: '{ label="value"}' }],
|
||||
targets: [{ expr: '{label="value"}' }],
|
||||
});
|
||||
|
||||
expect(datasources.elastic.query).toBeCalledTimes(1);
|
||||
@ -213,11 +226,11 @@ describe('Wrapper', () => {
|
||||
});
|
||||
|
||||
it('can close a pane from a split', async () => {
|
||||
const urlParams = {
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { refId: 'A' }]),
|
||||
right: JSON.stringify(['now-1h', 'now', 'elastic', { refId: 'A' }]),
|
||||
};
|
||||
setupExplore({ urlParams });
|
||||
const urlParams = defaultUrlParams({ rightDatasource: 'elastic' });
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
(datasources.elastic.query as Mock).mockReturnValueOnce(makeLogsQueryResponse());
|
||||
|
||||
const closeButtons = await screen.findAllByTitle(/Close split pane/i);
|
||||
await userEvent.click(closeButtons[1]);
|
||||
|
||||
@ -228,65 +241,55 @@ describe('Wrapper', () => {
|
||||
});
|
||||
|
||||
it('handles url change to split view', async () => {
|
||||
const urlParams = {
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
|
||||
};
|
||||
const urlParams = defaultUrlParams({});
|
||||
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
(datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
|
||||
locationService.partial({
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
|
||||
right: JSON.stringify(['now-1h', 'now', 'elastic', { expr: 'error' }]),
|
||||
});
|
||||
locationService.partial(defaultUrlParams({ rightDatasource: 'elastic', rightExprValue: 'error' }));
|
||||
|
||||
// Editor renders the new query
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
await screen.findByText(`elastic Editor input: error`);
|
||||
});
|
||||
|
||||
it('handles opening split with split open func', async () => {
|
||||
const urlParams = {
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
|
||||
};
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources, store } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
(datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
|
||||
// This is mainly to wait for render so that the left pane state is initialized as that is needed for splitOpen
|
||||
// to work
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
|
||||
store.dispatch(splitOpen<any>({ datasourceUid: 'elastic', query: { expr: 'error' } }) as any);
|
||||
|
||||
// Editor renders the new query
|
||||
await screen.findByText(`elastic Editor input: error`);
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
});
|
||||
|
||||
it('changes the document title of the explore page to include the datasource in use', async () => {
|
||||
const urlParams = {
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
|
||||
};
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
// This is mainly to wait for render so that the left pane state is initialized as that is needed for the title
|
||||
// to include the datasource
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
|
||||
await waitFor(() => expect(document.title).toEqual('Explore - loki - Grafana'));
|
||||
});
|
||||
it('changes the document title to include the two datasources in use in split view mode', async () => {
|
||||
const urlParams = {
|
||||
left: JSON.stringify(['now-1h', 'now', 'loki', { expr: '{ label="value"}' }]),
|
||||
};
|
||||
const urlParams = defaultUrlParams({});
|
||||
const { datasources, store } = setupExplore({ urlParams });
|
||||
(datasources.loki.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
(datasources.elastic.query as Mock).mockReturnValue(makeLogsQueryResponse());
|
||||
|
||||
// This is mainly to wait for render so that the left pane state is initialized as that is needed for splitOpen
|
||||
// to work
|
||||
await screen.findByText(`loki Editor input: { label="value"}`);
|
||||
await screen.findByText(`loki Editor input: {label="value"}`);
|
||||
|
||||
store.dispatch(splitOpen<any>({ datasourceUid: 'elastic', query: { expr: 'error' } }) as any);
|
||||
await waitFor(() => expect(document.title).toEqual('Explore - loki | elastic - Grafana'));
|
||||
|
Loading…
Reference in New Issue
Block a user