From 5ec9adb7a77cd36d9f3d1f01c21864bb12657f87 Mon Sep 17 00:00:00 2001 From: David Kaltschmidt Date: Fri, 5 Oct 2018 17:09:40 +0200 Subject: [PATCH] Explore: compact state URLs - allow positional state array in URL - key-based parsing as fallback - fix issue where split state was kept in URL after closing split --- public/app/core/utils/explore.test.ts | 53 ++++++++++++++++++++++++- public/app/core/utils/explore.ts | 25 +++++++++++- public/app/features/explore/Explore.tsx | 12 +++--- public/app/features/explore/Wrapper.tsx | 9 ++++- 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/public/app/core/utils/explore.test.ts b/public/app/core/utils/explore.test.ts index c47321225fe..915b47e14e2 100644 --- a/public/app/core/utils/explore.test.ts +++ b/public/app/core/utils/explore.test.ts @@ -36,14 +36,40 @@ describe('state functions', () => { range: DEFAULT_RANGE, }); }); + + it('returns a valid Explore state from URL parameter', () => { + const paramValue = + '%7B"datasource":"Local","queries":%5B%7B"query":"metric"%7D%5D,"range":%7B"from":"now-1h","to":"now"%7D%7D'; + expect(parseUrlState(paramValue)).toMatchObject({ + datasource: 'Local', + queries: [{ query: 'metric' }], + range: { + from: 'now-1h', + to: 'now', + }, + }); + }); + + it('returns a valid Explore state from a compact URL parameter', () => { + const paramValue = '%5B"now-1h","now","Local","metric"%5D'; + expect(parseUrlState(paramValue)).toMatchObject({ + datasource: 'Local', + queries: [{ query: 'metric' }], + range: { + from: 'now-1h', + to: 'now', + }, + }); + }); }); + describe('serializeStateToUrlParam', () => { it('returns url parameter value for a state object', () => { const state = { ...DEFAULT_EXPLORE_STATE, datasourceName: 'foo', range: { - from: 'now - 5h', + from: 'now-5h', to: 'now', }, queries: [ @@ -57,10 +83,33 @@ describe('state functions', () => { }; expect(serializeStateToUrlParam(state)).toBe( '{"datasource":"foo","queries":[{"query":"metric{test=\\"a/b\\"}"},' + - '{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now - 5h","to":"now"}}' + '{"query":"super{foo=\\"x/z\\"}"}],"range":{"from":"now-5h","to":"now"}}' + ); + }); + + it('returns url parameter value for a state object', () => { + const state = { + ...DEFAULT_EXPLORE_STATE, + datasourceName: 'foo', + range: { + from: 'now-5h', + to: 'now', + }, + queries: [ + { + query: 'metric{test="a/b"}', + }, + { + query: 'super{foo="x/z"}', + }, + ], + }; + expect(serializeStateToUrlParam(state, true)).toBe( + '["now-5h","now","foo","metric{test=\\"a/b\\"}","super{foo=\\"x/z\\"}"]' ); }); }); + describe('interplay', () => { it('can parse the serialized state into the original state', () => { const state = { diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index cca841a1725..ecd11a495ad 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -60,7 +60,20 @@ export async function getExploreUrl( export function parseUrlState(initial: string | undefined): ExploreUrlState { if (initial) { try { - return JSON.parse(decodeURI(initial)); + const parsed = JSON.parse(decodeURI(initial)); + if (Array.isArray(parsed)) { + if (parsed.length <= 3) { + throw new Error('Error parsing compact URL state for Explore.'); + } + const range = { + from: parsed[0], + to: parsed[1], + }; + const datasource = parsed[2]; + const queries = parsed.slice(3).map(query => ({ query })); + return { datasource, queries, range }; + } + return parsed; } catch (e) { console.error(e); } @@ -68,11 +81,19 @@ export function parseUrlState(initial: string | undefined): ExploreUrlState { return { datasource: null, queries: [], range: DEFAULT_RANGE }; } -export function serializeStateToUrlParam(state: ExploreState): string { +export function serializeStateToUrlParam(state: ExploreState, compact?: boolean): string { const urlState: ExploreUrlState = { datasource: state.datasourceName, queries: state.queries.map(q => ({ query: q.query })), range: state.range, }; + if (compact) { + return JSON.stringify([ + urlState.range.from, + urlState.range.to, + urlState.datasource, + ...urlState.queries.map(q => q.query), + ]); + } return JSON.stringify(urlState); } diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index c90ba0dd7cc..2ee9ba3bca3 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -275,7 +275,6 @@ export class Explore extends React.PureComponent { const { onChangeSplit } = this.props; if (onChangeSplit) { onChangeSplit(false); - this.saveState(); } }; @@ -292,7 +291,6 @@ export class Explore extends React.PureComponent { if (onChangeSplit) { const state = this.cloneState(); onChangeSplit(true, state); - this.saveState(); } }; @@ -534,12 +532,12 @@ export class Explore extends React.PureComponent { ) : ( -
- -
- )} + + )} {!datasourceMissing ? (