Просмотр исходного кода

feat(discover): shareable URL query (#9038)

* feat(discover): Add query to URL on success
Ayesha Omarali 6 лет назад
Родитель
Сommit
bf1c9aa6ec

+ 15 - 3
src/sentry/static/sentry/app/views/organizationDiscover/discover.jsx

@@ -2,6 +2,7 @@ import {Flex, Box} from 'grid-emotion';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import React from 'react';
 import React from 'react';
 import styled from 'react-emotion';
 import styled from 'react-emotion';
+import {browserHistory} from 'react-router';
 
 
 import {addErrorMessage} from 'app/actionCreators/indicator';
 import {addErrorMessage} from 'app/actionCreators/indicator';
 import {t} from 'app/locale';
 import {t} from 'app/locale';
@@ -22,6 +23,8 @@ import {isValidCondition} from './conditions/utils';
 import {isValidAggregation} from './aggregations/utils';
 import {isValidAggregation} from './aggregations/utils';
 import {Fieldset, PlaceholderText} from './styles';
 import {Fieldset, PlaceholderText} from './styles';
 
 
+import {getQueryStringFromQuery} from './utils';
+
 export default class OrganizationDiscover extends React.Component {
 export default class OrganizationDiscover extends React.Component {
   static propTypes = {
   static propTypes = {
     organization: SentryTypes.Organization,
     organization: SentryTypes.Organization,
@@ -41,7 +44,7 @@ export default class OrganizationDiscover extends React.Component {
   };
   };
 
 
   runQuery = () => {
   runQuery = () => {
-    const {queryBuilder} = this.props;
+    const {queryBuilder, organization} = this.props;
     // Strip any invalid conditions and aggregations
     // Strip any invalid conditions and aggregations
     const {conditions, aggregations} = queryBuilder.getInternal();
     const {conditions, aggregations} = queryBuilder.getInternal();
     const filteredConditions = conditions.filter(condition =>
     const filteredConditions = conditions.filter(condition =>
@@ -60,8 +63,17 @@ export default class OrganizationDiscover extends React.Component {
       this.updateField('aggregations', filteredAggregations);
       this.updateField('aggregations', filteredAggregations);
     }
     }
 
 
-    this.props.queryBuilder.fetch().then(
-      result => this.setState({result}),
+    queryBuilder.fetch().then(
+      result => {
+        const query = queryBuilder.getInternal();
+        this.setState({result});
+
+        browserHistory.push({
+          pathname: `/organizations/${organization.slug}/discover/${getQueryStringFromQuery(
+            query
+          )}`,
+        });
+      },
       () => {
       () => {
         addErrorMessage(t('An error occurred'));
         addErrorMessage(t('An error occurred'));
         this.setState({result: null});
         this.setState({result: null});

+ 8 - 1
src/sentry/static/sentry/app/views/organizationDiscover/index.jsx

@@ -7,6 +7,8 @@ import LoadingIndicator from 'app/components/loadingIndicator';
 import Discover from './discover';
 import Discover from './discover';
 import createQueryBuilder from './queryBuilder';
 import createQueryBuilder from './queryBuilder';
 
 
+import {getQueryFromQueryString} from './utils';
+
 const OrganizationDiscoverContainer = createReactClass({
 const OrganizationDiscoverContainer = createReactClass({
   displayName: 'OrganizationDiscoverContainer',
   displayName: 'OrganizationDiscoverContainer',
   mixins: [OrganizationState],
   mixins: [OrganizationState],
@@ -18,7 +20,12 @@ const OrganizationDiscoverContainer = createReactClass({
   },
   },
 
 
   componentDidMount: function() {
   componentDidMount: function() {
-    this.queryBuilder = createQueryBuilder({}, this.context.organization);
+    const query = this.props.location.search;
+
+    this.queryBuilder = createQueryBuilder(
+      getQueryFromQueryString(query),
+      this.context.organization
+    );
     this.queryBuilder.load().then(() => {
     this.queryBuilder.load().then(() => {
       this.setState({isLoading: false});
       this.setState({isLoading: false});
     });
     });

+ 25 - 0
src/sentry/static/sentry/app/views/organizationDiscover/utils.jsx

@@ -0,0 +1,25 @@
+export function getQueryFromQueryString(queryString) {
+  if (!queryString) {
+    return {};
+  }
+  let parsedQuery = queryString;
+  let result = {};
+  parsedQuery = parsedQuery.replace(/^\?|\/$/g, '').split('&');
+  parsedQuery.forEach(item => {
+    if (item.includes('=')) {
+      let key = item.split('=')[0];
+      let value = JSON.parse(decodeURIComponent(item.split('=')[1]));
+      result[key] = value;
+    }
+  });
+
+  return result;
+}
+
+export function getQueryStringFromQuery(query) {
+  const queryProperties = Object.entries(query).map(([key, value]) => {
+    return key + '=' + encodeURIComponent(JSON.stringify(value));
+  });
+
+  return `?${queryProperties.join('&')}`;
+}

+ 34 - 0
tests/js/spec/views/organizationDiscover/utils.spec.jsx

@@ -0,0 +1,34 @@
+import {
+  getQueryFromQueryString,
+  getQueryStringFromQuery,
+} from 'app/views/organizationDiscover/utils';
+
+const queryString =
+  '?aggregations=%5B%5B%22count()%22%2Cnull%2C%22count%22%5D%2C%5B%22topK(5)%22%2C%22os_build%22%2C%22topK_5_os_build%22%5D%5D&conditions=%5B%5D&end=%222018-07-10T01%3A18%3A04%22&fields=%5B%22event_id%22%2C%22timestamp%22%5D&limit=1000&orderby=%22-timestamp%22&projects=%5B8%5D&start=%222018-06-26T01%3A18%3A04%22';
+
+const query = {
+  aggregations: [['count()', null, 'count'], ['topK(5)', 'os_build', 'topK_5_os_build']],
+  conditions: [],
+  end: '2018-07-10T01:18:04',
+  fields: ['event_id', 'timestamp'],
+  limit: 1000,
+  orderby: '-timestamp',
+  projects: [8],
+  start: '2018-06-26T01:18:04',
+};
+
+describe('get query from URL query string', function() {
+  it('returns empty object if empty query string', function() {
+    expect(getQueryFromQueryString('')).toEqual({});
+  });
+
+  it('handles aggregations', function() {
+    expect(getQueryFromQueryString(queryString)).toEqual(query);
+  });
+});
+
+describe('get query URL string from query', function() {
+  it('parses query from query string', function() {
+    expect(getQueryStringFromQuery(query)).toEqual(queryString);
+  });
+});