소스 검색

Merge pull request #13540 from grafana/davkal/explore-compact-url-state

Explore: compact state URLs
David 7 년 전
부모
커밋
352961b3d7
4개의 변경된 파일82개의 추가작업 그리고 7개의 파일을 삭제
  1. 51 2
      public/app/core/utils/explore.test.ts
  2. 23 2
      public/app/core/utils/explore.ts
  3. 0 2
      public/app/features/explore/Explore.tsx
  4. 8 1
      public/app/features/explore/Wrapper.tsx

+ 51 - 2
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 = {

+ 23 - 2
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);
 }

+ 0 - 2
public/app/features/explore/Explore.tsx

@@ -275,7 +275,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     const { onChangeSplit } = this.props;
     if (onChangeSplit) {
       onChangeSplit(false);
-      this.saveState();
     }
   };
 
@@ -292,7 +291,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
     if (onChangeSplit) {
       const state = this.cloneState();
       onChangeSplit(true, state);
-      this.saveState();
     }
   };
 

+ 8 - 1
public/app/features/explore/Wrapper.tsx

@@ -38,10 +38,17 @@ export class Wrapper extends Component<WrapperProps, WrapperState> {
 
   onChangeSplit = (split: boolean, splitState: ExploreState) => {
     this.setState({ split, splitState });
+    // When closing split, remove URL state for split part
+    if (!split) {
+      delete this.urlStates[STATE_KEY_RIGHT];
+      this.props.updateLocation({
+        query: this.urlStates,
+      });
+    }
   };
 
   onSaveState = (key: string, state: ExploreState) => {
-    const urlState = serializeStateToUrlParam(state);
+    const urlState = serializeStateToUrlParam(state, true);
     this.urlStates[key] = urlState;
     this.props.updateLocation({
       query: this.urlStates,