Skip to content

Commit

Permalink
Do not throw any error when fetchServiceOperations fails
Browse files Browse the repository at this point in the history
This can happen when services are loaded from store

Signed-off-by: Yuri Roncella <[email protected]>
  • Loading branch information
Yuri Roncella committed Feb 12, 2019
1 parent 0f71029 commit 6e845f0
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 5 deletions.
7 changes: 7 additions & 0 deletions packages/jaeger-ui/src/actions/jaeger-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import { createAction } from 'redux-actions';
import JaegerAPI from '../api/jaeger';
import fileReader from '../utils/fileReader';

export const fetchTrace = createAction(
'@JAEGER_API/FETCH_TRACE',
Expand Down Expand Up @@ -50,3 +51,9 @@ export const fetchServiceOperations = createAction(
export const fetchDependencies = createAction('@JAEGER_API/FETCH_DEPENDENCIES', () =>
JaegerAPI.fetchDependencies()
);

export const uploadTraces = createAction(
'@JAEGER_API/UPLOAD_TRACES',
fileList => fileReader.readJSONFile(fileList),
fileList => ({ fileList })
);
20 changes: 20 additions & 0 deletions packages/jaeger-ui/src/actions/jaeger-api.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import isPromise from 'is-promise';

import * as jaegerApiActions from './jaeger-api';
import JaegerAPI from '../api/jaeger';
import fileReader from '../utils/fileReader';

it('@JAEGER_API/FETCH_TRACE should fetch the trace by id', () => {
const api = JaegerAPI;
Expand Down Expand Up @@ -114,3 +115,22 @@ it('@JAEGER_API/FETCH_SERVICE_OPERATIONS should call the JaegerAPI', () => {
expect(called.verify()).toBeTruthy();
mock.restore();
});

it('uploadTraces should return a promise', () => {
const fileList = { data: {}, filename: 'whatever' };

const { payload } = jaegerApiActions.uploadTraces(fileList);
expect(isPromise(payload)).toBeTruthy();
});

it('uploadTraces should call readJSONFile', () => {
const fileList = { data: {}, filename: 'whatever' };
const mock = sinon.mock(fileReader);
const called = mock
.expects('readJSONFile')
.once()
.withExactArgs(fileList);
jaegerApiActions.uploadTraces(fileList);
expect(called.verify()).toBeTruthy();
mock.restore();
});
2 changes: 1 addition & 1 deletion packages/jaeger-ui/src/api/jaeger.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const JaegerAPI = {
return getJSON(`${this.apiRoot}services`).catch(() => []);
},
fetchServiceOperations(serviceName) {
return getJSON(`${this.apiRoot}services/${encodeURIComponent(serviceName)}/operations`);
return getJSON(`${this.apiRoot}services/${encodeURIComponent(serviceName)}/operations`).catch(() => []);
},
fetchDependencies(endTs = new Date().getTime(), lookback = DEFAULT_DEPENDENCY_LOOKBACK) {
return getJSON(`${this.apiRoot}dependencies`, { query: { endTs, lookback } });
Expand Down
19 changes: 19 additions & 0 deletions packages/jaeger-ui/src/api/jaeger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ it('fetchServices() returns [] on a >= 400 status code', done => {
});
});

it('fetchOperations() returns [] on a >= 400 status code', done => {
const status = 400;
const statusText = 'some-status';
const msg = 'some-message';
const errorData = { errors: [{ msg, code: status }] };

fetchMock.mockReturnValue(
Promise.resolve({
status,
statusText,
text: () => Promise.resolve(JSON.stringify(errorData)),
})
);
JaegerAPI.fetchServiceOperations().then(operations => {
expect(operations).toEqual([]);
done();
});
});

it('fetchTrace() throws an useful error derived from a text payload', done => {
const status = 400;
const statusText = 'some-status';
Expand Down
49 changes: 49 additions & 0 deletions packages/jaeger-ui/src/components/SearchTracePage/FileUploader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import * as React from 'react';
import { Upload, Icon } from 'antd';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import * as jaegerApiActions from '../../actions/jaeger-api';

const Dragger = Upload.Dragger;

export function FileUploaderImpl(props) {
const { uploadTraces } = props;
return (
<Dragger accept=".json" customRequest={uploadTraces} multiple>
<p className="ant-upload-drag-icon">
<Icon type="inbox" />
</p>
<p className="ant-upload-text">Click or drag files to this area.</p>
<p className="ant-upload-hint">Support JSON files containig one or more traces.</p>
</Dragger>
);
}

FileUploaderImpl.propTypes = {
uploadTraces: PropTypes.func.isRequired,
};

function mapDispatchToProps(dispatch) {
const { uploadTraces } = bindActionCreators(jaegerApiActions, dispatch);
return {
uploadTraces,
};
}

export default connect(null, mapDispatchToProps)(FileUploaderImpl);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import React from 'react';
import { mount } from 'enzyme';

import { FileUploaderImpl as FileUploader } from './FileUploader';

describe('<FileUploader />', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(<FileUploader />);
});

it('does not explode', () => {
expect(wrapper).toBeDefined();
});
});
15 changes: 12 additions & 3 deletions packages/jaeger-ui/src/components/SearchTracePage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/* eslint-disable react/require-default-props */

import React, { Component } from 'react';
import { Col, Row } from 'antd';
import { Col, Row, Tabs } from 'antd';
import PropTypes from 'prop-types';
import queryString from 'query-string';
import { connect } from 'react-redux';
Expand All @@ -34,10 +34,13 @@ import { fetchedState } from '../../constants';
import { sortTraces } from '../../model/search';
import getLastXformCacher from '../../utils/get-last-xform-cacher';
import { stripEmbeddedState } from '../../utils/embedded-url';
import FileUploader from './FileUploader';

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

const TabPane = Tabs.TabPane;

// export for tests
export class SearchTracePageImpl extends Component {
componentDidMount() {
Expand Down Expand Up @@ -95,8 +98,14 @@ export class SearchTracePageImpl extends Component {
{!embedded && (
<Col span={6} className="SearchTracePage--column">
<div className="SearchTracePage--find">
<h2>Find Traces</h2>
{!loadingServices && services ? <SearchForm services={services} /> : <LoadingIndicator />}
<Tabs size="large">
<TabPane tab="Find Traces" key="searchForm">
{!loadingServices && services ? <SearchForm services={services} /> : <LoadingIndicator />}
</TabPane>
<TabPane tab="Load from File" key="fileUploader">
<FileUploader />
</TabPane>
</Tabs>
</div>
</Col>
)}
Expand Down
34 changes: 33 additions & 1 deletion packages/jaeger-ui/src/reducers/trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import _isEqual from 'lodash/isEqual';
import { handleActions } from 'redux-actions';

import { fetchTrace, fetchMultipleTraces, searchTraces } from '../actions/jaeger-api';
import { fetchTrace, fetchMultipleTraces, searchTraces, uploadTraces } from '../actions/jaeger-api';
import { fetchedState } from '../constants';
import transformTraceData from '../model/transform-trace-data';

Expand Down Expand Up @@ -124,6 +124,34 @@ function searchErred(state, { meta, payload }) {
return { ...state, search };
}

function uploadStarted(state) {
const search = {
results: [].concat(state.search.results),
state: fetchedState.LOADING,
};
return { ...state, search };
}

function uploadDone(state, { payload }) {
const processed = payload.data.map(transformTraceData);
const resultTraces = {};
const results = [].concat(state.search.results);
for (let i = 0; i < processed.length; i++) {
const data = processed[i];
const id = data.traceID;
resultTraces[id] = { data, id, state: fetchedState.DONE };
results.push(id);
}
const traces = { ...state.traces, ...resultTraces };
const search = { ...state.search, results, state: fetchedState.DONE };
return { ...state, search, traces };
}

function uploadErred(state, { payload }) {
const search = { error: payload, results: [], state: fetchedState.ERROR };
return { ...state, search };
}

export default handleActions(
{
[`${fetchTrace}_PENDING`]: fetchTraceStarted,
Expand All @@ -137,6 +165,10 @@ export default handleActions(
[`${searchTraces}_PENDING`]: fetchSearchStarted,
[`${searchTraces}_FULFILLED`]: searchDone,
[`${searchTraces}_REJECTED`]: searchErred,

[`${uploadTraces}_PENDING`]: uploadStarted,
[`${uploadTraces}_FULFILLED`]: uploadDone,
[`${uploadTraces}_REJECTED`]: uploadErred,
},
initialState
);
52 changes: 52 additions & 0 deletions packages/jaeger-ui/src/reducers/trace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,55 @@ describe('search traces', () => {
});
});
});

describe('upload traces', () => {
it('handles a pending upload request', () => {
const state = traceReducer(
{ search: { results: [id] } },
{
type: `${jaegerApiActions.uploadTraces}${ACTION_POSTFIX_PENDING}`,
}
);
const outcome = {
results: [id],
state: fetchedState.LOADING,
};
expect(state.search).toEqual(outcome);
});

it('handles a successful upload request', () => {
const state = traceReducer(undefined, {
type: `${jaegerApiActions.uploadTraces}${ACTION_POSTFIX_FULFILLED}`,
payload: { data: [trace] },
});
const outcome = {
traces: {
[id]: {
id,
data: transformTraceData(trace),
state: fetchedState.DONE,
},
},
search: {
query: null,
state: fetchedState.DONE,
results: [id],
},
};
expect(state).toEqual(outcome);
});

it('handles a failed upload request', () => {
const error = 'some-error';
const state = traceReducer(undefined, {
type: `${jaegerApiActions.uploadTraces}${ACTION_POSTFIX_REJECTED}`,
payload: error,
});
const outcome = {
error,
results: [],
state: fetchedState.ERROR,
};
expect(state.search).toEqual(outcome);
});
});
33 changes: 33 additions & 0 deletions packages/jaeger-ui/src/utils/fileReader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const fileReader = {
readJSONFile(fileList) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => {
resolve(reader.result);
};
reader.onerror = () => {
reject();
};
reader.onabort = () => {
reject();
};
reader.readAsText(fileList.file);
}).then(result => JSON.parse(result));
},
};

export default fileReader;
Loading

0 comments on commit 6e845f0

Please sign in to comment.