Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify stored search settings #111

Merged
merged 3 commits into from
Nov 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/components/SearchTracePage/TraceSearchForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,19 @@ const mapStateToProps = state => {
let lastSearchOperation;

if (lastSearch) {
lastSearchService = lastSearch.service;
lastSearchOperation = lastSearch.operation;
// last search is only valid if the service is in the list of services
const { operation: lastOp, service: lastSvc } = lastSearch;
if (lastSvc && lastSvc !== '-') {
if (state.services.services.includes(lastSvc)) {
lastSearchService = lastSvc;
if (lastOp && lastOp !== '-') {
const ops = state.services.operationsForService[lastSvc];
if (lastOp === 'all' || (ops && ops.includes(lastOp))) {
lastSearchOperation = lastOp;
}
}
}
}
}

const {
Expand All @@ -242,6 +253,7 @@ const mapStateToProps = state => {
if (traceIDParams) {
traceIDs = traceIDParams instanceof Array ? traceIDParams.join(',') : traceIDParams;
}

return {
destroyOnUnmount: false,
initialValues: {
Expand Down
81 changes: 53 additions & 28 deletions src/components/SearchTracePage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import React, { Component } from 'react';
import _values from 'lodash/values';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import queryString from 'query-string';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import store from 'store';

import JaegerLogo from '../../img/jaeger-logo.svg';

Expand Down Expand Up @@ -61,35 +62,45 @@ const traceResultsFiltersFormSelector = formValueSelector('traceResultsFilters')

export default class SearchTracePage extends Component {
componentDidMount() {
const { searchTraces, urlQueryParams, fetchServices } = this.props;
const { searchTraces, urlQueryParams, fetchServices, fetchServiceOperations } = this.props;
if (urlQueryParams.service || urlQueryParams.traceID) {
searchTraces(urlQueryParams);
}
fetchServices();
const { service } = store.get('lastSearch') || {};
if (service && service !== '-') {
fetchServiceOperations(service);
}
}

render() {
const {
traceResults,
services,
numberOfTraceResults,
maxTraceDuration,
loading,
errorMessage,
isHomepage,
loadingServices,
loadingTraces,
maxTraceDuration,
numberOfTraceResults,
services,
traceResults,
} = this.props;
const hasTraceResults = traceResults && traceResults.length > 0;
return (
<div className="trace-search ui grid padded">
<div className="four wide column">
<div className="ui tertiary segment" style={{ background: 'whitesmoke' }}>
<h3>Find Traces</h3>
<TraceSearchForm services={services} />
{!loadingServices && services
? <TraceSearchForm services={services} />
: <div className="m1">
<div className="ui active centered inline loader" />
</div>}
</div>
</div>
<div className="twelve wide column padded">
{loading && <div className="ui active centered inline loader" />}
{loadingTraces && <div className="ui active centered inline loader" />}
{errorMessage &&
!loading &&
!loadingTraces &&
<div className="ui message red trace-search--error">
There was an error querying for traces:<br />
{errorMessage}
Expand All @@ -103,11 +114,11 @@ export default class SearchTracePage extends Component {
</div>}
{!isHomepage &&
!hasTraceResults &&
!loading &&
!loadingTraces &&
!errorMessage &&
<div className="ui message trace-search--no-results">No trace results. Try another query.</div>}
{hasTraceResults &&
!loading &&
!loadingTraces &&
<div>
<div>
<div style={{ border: '1px solid #e6e6e6' }}>
Expand Down Expand Up @@ -165,7 +176,8 @@ SearchTracePage.propTypes = {
traceResults: PropTypes.array,
numberOfTraceResults: PropTypes.number,
maxTraceDuration: PropTypes.number,
loading: PropTypes.bool,
loadingServices: PropTypes.bool,
loadingTraces: PropTypes.bool,
urlQueryParams: PropTypes.shape({
service: PropTypes.string,
limit: PropTypes.string,
Expand All @@ -180,30 +192,38 @@ SearchTracePage.propTypes = {
history: PropTypes.shape({
push: PropTypes.func,
}),
fetchServiceOperations: PropTypes.func,
fetchServices: PropTypes.func,
errorMessage: PropTypes.string,
};

const stateTraceXformer = getLastXformCacher(stateTrace => {
const { traces: traceMap, loading, error: traceError } = stateTrace;
const { traces: traceMap, loading: loadingTraces, error: traceError } = stateTrace;
const { traces, maxDuration } = getTraceSummaries(_values(traceMap));
return { traces, maxDuration, loading, traceError };
return { traces, maxDuration, traceError, loadingTraces };
});

const stateServicesXformer = getLastXformCacher(stateServices => {
const { services: serviceList, operationsForService: opsBySvc, error: serviceError } = stateServices;
const services = serviceList.map(name => ({
name,
operations: opsBySvc[name] || [],
}));
return { services, serviceError };
const {
loading: loadingServices,
services: serviceList,
operationsForService: opsBySvc,
error: serviceError,
} = stateServices;
const services =
serviceList &&
serviceList.map(name => ({
name,
operations: opsBySvc[name] || [],
}));
return { loadingServices, services, serviceError };
});

function mapStateToProps(state) {
const query = queryString.parse(state.routing.location.search);
const isHomepage = !Object.keys(query).length;
const { traces, maxDuration, loading, traceError } = stateTraceXformer(state.trace);
const { services, serviceError } = stateServicesXformer(state.services);
const { traces, maxDuration, traceError, loadingTraces } = stateTraceXformer(state.trace);
const { loadingServices, services, serviceError } = stateServicesXformer(state.services);
const errorMessage = serviceError || traceError ? `${serviceError || ''} ${traceError || ''}` : '';
const sortBy = traceResultsFiltersFormSelector(state, 'sortBy');
sortTraces(traces, sortBy);
Expand All @@ -216,16 +236,21 @@ function mapStateToProps(state) {
maxTraceDuration: maxDuration,
urlQueryParams: query,
services,
loading,
loadingTraces,
loadingServices,
errorMessage,
};
}

function mapDispatchToProps(dispatch) {
const { searchTraces, fetchServices } = bindActionCreators(jaegerApiActions, dispatch);
const { searchTraces, fetchServices, fetchServiceOperations } = bindActionCreators(
jaegerApiActions,
dispatch
);
return {
searchTraces,
fetchServiceOperations,
fetchServices,
searchTraces,
};
}
export const ConnectedSearchTracePage = connect(mapStateToProps, mapDispatchToProps)(SearchTracePage);
11 changes: 5 additions & 6 deletions src/reducers/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { fetchServices, fetchServiceOperations as fetchOps } from '../actions/ja
import { baseStringComparator } from '../utils/sort';

const initialState = {
services: [],
// `services` initial value of `null` indicates they haven't yet been loaded
services: null,
operationsForService: {},
loading: false,
error: null,
Expand All @@ -29,10 +30,8 @@ function fetchStarted(state) {
}

function fetchServicesDone(state, { payload }) {
const services = payload.data;
if (Array.isArray(services)) {
services.sort(baseStringComparator);
}
const services = payload.data || [];
services.sort(baseStringComparator);
return { ...state, services, error: null, loading: false };
}

Expand All @@ -50,7 +49,7 @@ function fetchOpsDone(state, { meta, payload }) {
if (Array.isArray(operations)) {
operations.sort(baseStringComparator);
}
const operationsForService = { ...state.operationsForService, [meta.serviceName]: operations };
const operationsForService = { ...state.operationsForService, [meta.serviceName]: operations || [] };
return { ...state, operationsForService };
}

Expand Down
27 changes: 18 additions & 9 deletions src/reducers/services.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const initialState = serviceReducer(undefined, {});

function verifyInitialState() {
expect(initialState).toEqual({
services: [],
services: null,
loading: false,
error: null,
operationsForService: {},
Expand All @@ -29,18 +29,28 @@ function verifyInitialState() {
beforeEach(verifyInitialState);
afterEach(verifyInitialState);

it('should handle a fetch services with loading state', () => {
it('#92 - ensures services is at least an empty array', () => {
const services = null;
const state = serviceReducer(initialState, {
type: `${fetchServices}_PENDING`,
type: `${fetchServices}_FULFILLED`,
payload: { data: services },
});
expect(state).toEqual({
services: [],
operationsForService: {},
loading: true,
loading: false,
error: null,
});
});

it('should handle a fetch services with loading state', () => {
const state = serviceReducer(initialState, {
type: `${fetchServices}_PENDING`,
});
const expected = { ...initialState, loading: true };
expect(state).toEqual(expected);
});

it('should handle successful services fetch', () => {
const services = ['a', 'b', 'c'];
const state = serviceReducer(initialState, {
Expand Down Expand Up @@ -76,12 +86,11 @@ it('should handle a successful fetching operations for a service ', () => {
meta: { serviceName: 'serviceA' },
payload: { data: ops.slice() },
});
expect(state).toEqual({
services: [],
const expected = {
...initialState,
operationsForService: {
serviceA: ops,
},
loading: false,
error: null,
});
};
expect(state).toEqual(expected);
});