Skip to content

Commit

Permalink
feat(environments): Add group events environment filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
lynnagara committed Feb 26, 2018
1 parent 1c3c01a commit 45194dc
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 41 deletions.
17 changes: 11 additions & 6 deletions src/sentry/static/sentry/app/components/searchBar.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';

class SearchBar extends React.PureComponent {
static propTypes = {
query: PropTypes.string,
defaultQuery: PropTypes.string,
onSearch: PropTypes.func,
onQueryChange: PropTypes.func,
placeholder: PropTypes.string,
};

static defaultProps = {
defaultQuery: '',
query: '',
onSearch: function() {},
onQueryChange: function() {},
};

constructor(...args) {
Expand All @@ -25,8 +22,16 @@ class SearchBar extends React.PureComponent {
};
}

componentWillReceiveProps(nextProps) {
if (nextProps.query !== this.props.query) {
this.setState({
query: nextProps.query,
});
}
}

blur = () => {
ReactDOM.findDOMNode(this.refs.searchInput).blur();
this.searchInput.blur();
};

onSubmit = evt => {
Expand Down Expand Up @@ -58,14 +63,14 @@ class SearchBar extends React.PureComponent {
render() {
return (
<div className="search">
<form className="form-horizontal" ref="searchForm" onSubmit={this.onSubmit}>
<form className="form-horizontal" onSubmit={this.onSubmit}>
<div>
<input
type="text"
className="search-input form-control"
placeholder={this.props.placeholder}
name="query"
ref="searchInput"
ref={el => (this.searchInput = el)}
autoComplete="off"
value={this.state.query}
onBlur={this.onQueryBlur}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// remove leading and trailing whitespace and remove double spaces
function formatQueryString(qs) {
export function formatQueryString(qs) {
return qs.trim().replace(/\s+/g, ' ');
}

// returns environment name from query or null if not specified
// Any charater can be valid in an environment name
function getQueryEnvironment(qs) {
export function getQueryEnvironment(qs) {
const match = qs.match(/environment:([^\s]*)/);
return match ? match[1] : null;
}

function getQueryStringWithEnvironment(qs, env) {
export function getQueryStringWithEnvironment(qs, env) {
const qsWithoutEnv = qs.replace(/environment:[^\s]*/g, '');
return formatQueryString(
env === null ? qsWithoutEnv : qsWithoutEnv + ` environment:${env}`
env === null ? qsWithoutEnv : `${qsWithoutEnv} environment:${env}`
);
}

Expand Down
88 changes: 63 additions & 25 deletions src/sentry/static/sentry/app/views/groupEvents.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {browserHistory} from 'react-router';

Expand All @@ -10,42 +11,90 @@ import Pagination from '../components/pagination';
import SearchBar from '../components/searchBar';
import EventsTable from '../components/eventsTable/eventsTable';
import {t} from '../locale';
import withEnvironment from '../utils/withEnvironment';
import {getQueryEnvironment, getQueryStringWithEnvironment} from '../utils/queryString';
import EnvironmentStore from '../stores/environmentStore';
import {setActiveEnvironment} from '../actionCreators/environments';

const GroupEvents = createReactClass({
displayName: 'GroupEvents',

propTypes: {
environment: PropTypes.object,
},

mixins: [ApiMixin, GroupState],

getInitialState() {
let queryParams = this.props.location.query;
return {
const queryParams = this.props.location.query;

const initialState = {
eventList: [],
loading: true,
error: false,
pageLinks: '',
query: queryParams.query || '',
};

// If an environment is specified in the query, update the global environment
// Otherwise if a global environment is present update the query
const queryEnvironment = EnvironmentStore.getByName(
getQueryEnvironment(queryParams.query || '')
);

if (queryEnvironment) {
setActiveEnvironment(queryEnvironment);
} else if (this.props.environment) {
initialState.query = getQueryStringWithEnvironment(
initialState.query,
this.props.environment.name
);
}

return initialState;
},

componentWillMount() {
this.fetchData();
},

componentWillReceiveProps(nextProps) {
if (
nextProps.params.groupId !== this.props.params.groupId ||
nextProps.location.search !== this.props.location.search
) {
let queryParams = nextProps.location.query;
// If query has changed, update the environment with the query environment
if (nextProps.location.search !== this.props.location.search) {
const queryParams = nextProps.location.query;

const queryEnvironment = EnvironmentStore.getByName(
getQueryEnvironment(queryParams.query || '')
);

if (queryEnvironment) {
setActiveEnvironment(queryEnvironment);
}

this.setState(
{
query: queryParams.query,
},
this.fetchData
);
}

// If environment has changed, update query with new environment
if (nextProps.environment !== this.props.environment) {
const newQueryString = getQueryStringWithEnvironment(
nextProps.location.query.query || '',
nextProps.environment ? nextProps.environment.name : null
);
this.setState(
{
query: newQueryString,
},
this.handleSearch(newQueryString)
);
}
},

onSearch(query) {
handleSearch(query) {
let targetQueryParams = {};
if (query !== '') targetQueryParams.query = query;

Expand All @@ -56,28 +105,17 @@ const GroupEvents = createReactClass({
});
},

getEndpoint() {
let params = this.props.params;
let queryParams = {
...this.props.location.query,
limit: 50,
query: this.state.query,
};

return `/issues/${params.groupId}/events/?${jQuery.param(queryParams)}`;
},

fetchData() {
let queryParams = this.props.location.query;

this.setState({
loading: true,
error: false,
});

this.api.request(this.getEndpoint(), {
const query = {limit: 50, query: this.state.query};

this.api.request(`/issues/${this.props.params.groupId}/events/`, {
query,
method: 'GET',
data: queryParams,
success: (data, _, jqXHR) => {
this.setState({
eventList: data,
Expand Down Expand Up @@ -155,7 +193,7 @@ const GroupEvents = createReactClass({
defaultQuery=""
placeholder={t('search event id, message, or tags')}
query={this.state.query}
onSearch={this.onSearch}
onSearch={this.handleSearch}
/>
</div>
{this.renderBody()}
Expand All @@ -164,4 +202,4 @@ const GroupEvents = createReactClass({
},
});

export default GroupEvents;
export default withEnvironment(GroupEvents);
10 changes: 5 additions & 5 deletions src/sentry/static/sentry/app/views/stream.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import StreamFilters from './stream/filters';
import StreamSidebar from './stream/sidebar';
import TimeSince from '../components/timeSince';
import utils from '../utils';
import streamUtils from './stream/utils';
import qsUtils from '../utils/queryString';
import {logAjaxError} from '../utils/logging';
import parseLinkHeader from '../utils/parseLinkHeader';
import {t, tn, tct} from '../locale';
Expand Down Expand Up @@ -370,7 +370,7 @@ const Stream = createReactClass({
let url = this.getGroupListEndpoint();

// Remove leading and trailing whitespace
let query = streamUtils.formatQueryString(this.state.query);
let query = qsUtils.formatQueryString(this.state.query);

let activeEnvironment = this.state.activeEnvironment;
let activeEnvName = activeEnvironment ? activeEnvironment.name : null;
Expand All @@ -385,7 +385,7 @@ const Stream = createReactClass({

// Always keep the global active environment in sync with the queried environment
// The global environment wins unless there one is specified by the saved search
const queryEnvironment = streamUtils.getQueryEnvironment(query);
const queryEnvironment = qsUtils.getQueryEnvironment(query);

if (queryEnvironment !== null) {
// Set the global environment to the one specified by the saved search
Expand All @@ -396,7 +396,7 @@ const Stream = createReactClass({
requestParams.environment = queryEnvironment;
} else if (activeEnvironment) {
// Set the environment of the query to match the global settings
query = streamUtils.getQueryStringWithEnvironment(query, activeEnvironment.name);
query = qsUtils.getQueryStringWithEnvironment(query, activeEnvironment.name);
requestParams.query = query;
requestParams.environment = activeEnvironment.name;
}
Expand Down Expand Up @@ -536,7 +536,7 @@ const Stream = createReactClass({
// the environment parameter is part of the saved search
let environment = context.environment;

let query = streamUtils.getQueryStringWithEnvironment(
let query = qsUtils.getQueryStringWithEnvironment(
this.state.query,
environment === null ? null : environment.name
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import utils from 'app/views/stream/utils';
import utils from 'app/utils/queryString';

describe('getQueryEnvironment()', function() {
it('returns environment name', function() {
Expand Down

0 comments on commit 45194dc

Please sign in to comment.