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

Trace diffs #228

Merged
merged 13 commits into from
Aug 14, 2018
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ npm-debug.log
.vscode
.idea
yarn-error.log

lerna-debug\.log
lerna-debug.log
3 changes: 3 additions & 0 deletions packages/jaeger-ui/config-overrides-antd-vars.less
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
@layout-zero-trigger-height : 42px;

@menu-dark-bg: #151515;

// Table
@table-row-hover-bg:#e5f2f2;
1 change: 1 addition & 0 deletions packages/jaeger-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"sinon": "^3.2.1"
},
"dependencies": {
"@jaegertracing/plexus": "0.0.1-dev.3",
"antd": "^3.0.3",
"chance": "^1.0.10",
"classnames": "^2.2.5",
Expand Down
6 changes: 6 additions & 0 deletions packages/jaeger-ui/src/actions/jaeger-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export const fetchTrace = createAction(
id => ({ id })
);

export const fetchMultipleTraces = createAction(
'@JAEGER_API/FETCH_MULTIPLE_TRACES',
ids => JaegerAPI.searchTraces({ traceID: ids }),
ids => ({ ids })
);

export const archiveTrace = createAction(
'@JAEGER_API/ARCHIVE_TRACE',
id => JaegerAPI.archiveTrace(id),
Expand Down
33 changes: 15 additions & 18 deletions packages/jaeger-ui/src/components/App/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,55 @@ import type { Location } from 'react-router-dom';
import { withRouter } from 'react-router-dom';

import TopNav from './TopNav';
import type { Config } from '../../types/config';
import { trackPageView } from '../../utils/tracking';

import './Page.css';

type PageProps = {
location: Location,
type Props = {
pathname: string,
search: string,
children: React.Node,
config: Config,
};

const { Header, Content } = Layout;

// export for tests
export class PageImpl extends React.Component<PageProps> {
props: PageProps;
export class PageImpl extends React.Component<Props> {
props: Props;

componentDidMount() {
const { pathname, search } = this.props.location;
const { pathname, search } = this.props;
trackPageView(pathname, search);
}

componentWillReceiveProps(nextProps: PageProps) {
const { pathname, search } = this.props.location;
const { pathname: nextPathname, search: nextSearch } = nextProps.location;
componentWillReceiveProps(nextProps: Props) {
const { pathname, search } = this.props;
const { pathname: nextPathname, search: nextSearch } = nextProps;
if (pathname !== nextPathname || search !== nextSearch) {
trackPageView(nextPathname, nextSearch);
}
}

render() {
const { children, config, location } = this.props;
const menu = config && config.menu;
return (
<div>
<Helmet title="Jaeger UI" />
<Layout>
<Header className="Page--topNav">
<TopNav activeKey={location.pathname} menuConfig={menu} />
<TopNav />
</Header>
<Content className="Page--content">{children}</Content>
<Content className="Page--content">{this.props.children}</Content>
</Layout>
</div>
);
}
}

// export for tests
export function mapStateToProps(state: { config: Config, router: { location: Location } }, ownProps: any) {
const { config } = state;
const { location } = state.router;
return { ...ownProps, config, location };
export function mapStateToProps(state: { router: { location: Location } }) {
const { pathname, search } = state.router.location;
return { pathname, search };
}

// export default withRouter(connect(mapStateToProps)(PageImpl));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove?

export default withRouter(connect(mapStateToProps)(PageImpl));
27 changes: 10 additions & 17 deletions packages/jaeger-ui/src/components/App/Page.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,12 @@ import { trackPageView } from '../../utils/tracking';

describe('mapStateToProps()', () => {
it('maps state to props', () => {
const pathname = 'a-pathname';
const search = 'a-search';
const state = {
config: {},
router: { location: {} },
router: { location: { pathname, search } },
};
const ownProps = { a: {} };
expect(mapStateToProps(state, ownProps)).toEqual({
config: state.config,
location: state.router.location,
a: ownProps.a,
});
expect(mapStateToProps(state)).toEqual({ pathname, search });
});
});

Expand All @@ -44,11 +40,8 @@ describe('<Page>', () => {
beforeEach(() => {
trackPageView.mockReset();
props = {
location: {
pathname: String(Math.random()),
search: String(Math.random()),
},
config: { menu: [] },
pathname: String(Math.random()),
search: String(Math.random()),
};
wrapper = mount(<Page {...props} />);
});
Expand All @@ -58,14 +51,14 @@ describe('<Page>', () => {
});

it('tracks an initial page-view', () => {
const { pathname, search } = props.location;
const { pathname, search } = props;
expect(trackPageView.mock.calls).toEqual([[pathname, search]]);
});

it('tracks a pageView when the location changes', () => {
trackPageView.mockReset();
const location = { pathname: 'le-path', search: 'searching' };
wrapper.setProps({ location });
expect(trackPageView.mock.calls).toEqual([[location.pathname, location.search]]);
props = { pathname: 'le-path', search: 'searching' };
wrapper.setProps(props);
expect(trackPageView.mock.calls).toEqual([[props.pathname, props.search]]);
});
});
61 changes: 41 additions & 20 deletions packages/jaeger-ui/src/components/App/TopNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,38 @@

import React from 'react';
import { Dropdown, Icon, Menu } from 'antd';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';

import TraceIDSearchInput from './TraceIDSearchInput';
import type { ConfigMenuItem, ConfigMenuGroup } from '../../types/config';
import * as dependencies from '../DependencyGraph/url';
import * as searchUrl from '../SearchTracePage/url';
import * as diffUrl from '../TraceDiff/url';
import { getConfigValue } from '../../utils/config/get-config';
import prefixUrl from '../../utils/prefix-url';

type TopNavProps = {
activeKey: string,
menuConfig: (ConfigMenuItem | ConfigMenuGroup)[],
};
import type { ReduxState } from '../../types';
import type { ConfigMenuItem, ConfigMenuGroup } from '../../types/config';

type Props = ReduxState;

const NAV_LINKS = [
{
to: prefixUrl('/search'),
to: searchUrl.getUrl(),
matches: searchUrl.matches,
text: 'Search',
},
{
to: (props: Props) => diffUrl.getUrl(props.traceDiff),
matches: diffUrl.matches,
text: 'Compare',
},
];

if (getConfigValue('dependencies.menuEnabled')) {
NAV_LINKS.push({
to: prefixUrl('/dependencies'),
to: dependencies.getUrl(),
matches: dependencies.matches,
text: 'Dependencies',
});
}
Expand Down Expand Up @@ -66,12 +76,13 @@ function CustomNavDropdown({ label, items }: ConfigMenuGroup) {
);
}

export default function TopNav(props: TopNavProps) {
const { activeKey, menuConfig } = props;
const menuItems = Array.isArray(menuConfig) ? menuConfig : [];
export function TopNavImpl(props: Props) {
const { config, router } = props;
const { pathname } = router.location;
const menuItems = Array.isArray(config.menu) ? config.menu : [];
return (
<div>
<Menu theme="dark" mode="horizontal" selectable={false} className="ub-right" selectedKeys={[activeKey]}>
<Menu theme="dark" mode="horizontal" selectable={false} className="ub-right" selectedKeys={[pathname]}>
{menuItems.map(m => {
if (m.items != null) {
const group = ((m: any): ConfigMenuGroup);
Expand All @@ -91,25 +102,35 @@ export default function TopNav(props: TopNavProps) {
);
})}
</Menu>
<Menu theme="dark" mode="horizontal" selectable={false} selectedKeys={[activeKey]}>
<Menu theme="dark" mode="horizontal" selectable={false} selectedKeys={[pathname]}>
<Menu.Item>
<Link to={prefixUrl('/')}>Jaeger UI</Link>
</Menu.Item>
<Menu.Item>
<TraceIDSearchInput />
</Menu.Item>
{NAV_LINKS.map(({ to, text }) => (
<Menu.Item key={to}>
<Link to={to}>{text}</Link>
</Menu.Item>
))}
{NAV_LINKS.map(({ matches, to, text }) => {
const url = typeof to === 'string' ? to : to(props);
const key = matches(pathname) ? pathname : url;
return (
<Menu.Item key={key}>
<Link to={url}>{text}</Link>
</Menu.Item>
);
})}
</Menu>
</div>
);
}

TopNav.defaultProps = {
TopNavImpl.defaultProps = {
menuConfig: [],
};

TopNav.CustomNavDropdown = CustomNavDropdown;
TopNavImpl.CustomNavDropdown = CustomNavDropdown;

function mapStateToProps(state: Props) {
return state;
}

export default withRouter(connect(mapStateToProps)(TopNavImpl));
27 changes: 16 additions & 11 deletions packages/jaeger-ui/src/components/App/TopNav.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { Link } from 'react-router-dom';

import TopNav from './TopNav';
import { TopNavImpl as TopNav } from './TopNav';

describe('<TopNav>', () => {
const labelGitHub = 'GitHub';
Expand All @@ -34,16 +34,21 @@ describe('<TopNav>', () => {
];

const defaultProps = {
menuConfig: [
{
label: labelGitHub,
url: githubUrl,
},
{
label: labelAbout,
items: dropdownItems,
},
],
config: {
menu: [
{
label: labelGitHub,
url: githubUrl,
},
{
label: labelAbout,
items: dropdownItems,
},
],
},
router: {
location: { location: { pathname: 'some-path ' } },
},
};

let wrapper;
Expand Down
26 changes: 17 additions & 9 deletions packages/jaeger-ui/src/components/App/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ import { ConnectedRouter } from 'react-router-redux';

import NotFound from './NotFound';
import Page from './Page';
import { ConnectedDependencyGraphPage } from '../DependencyGraph';
import { ConnectedSearchTracePage } from '../SearchTracePage';
import { ConnectedTracePage } from '../TracePage';
import DependencyGraph from '../DependencyGraph';
import { ROUTE_PATH as dependenciesPath } from '../DependencyGraph/url';
import SearchTracePage from '../SearchTracePage';
import { ROUTE_PATH as searchPath } from '../SearchTracePage/url';
import TraceDiff from '../TraceDiff';
import { ROUTE_PATH as traceDiffPath } from '../TraceDiff/url';
import TracePage from '../TracePage';
import { ROUTE_PATH as tracePath } from '../TracePage/url';
import JaegerAPI, { DEFAULT_API_ROOT } from '../../api/jaeger';
import configureStore from '../../utils/configure-store';
import prefixUrl from '../../utils/prefix-url';
Expand All @@ -45,12 +50,15 @@ export default class JaegerUIApp extends Component {
<ConnectedRouter history={history}>
<Page>
<Switch>
<Route path={prefixUrl('/search')} component={ConnectedSearchTracePage} />
<Route path={prefixUrl('/trace/:id')} component={ConnectedTracePage} />
<Route path={prefixUrl('/dependencies')} component={ConnectedDependencyGraphPage} />
<Redirect exact path="/" to={prefixUrl('/search')} />
<Redirect exact path={prefixUrl()} to={prefixUrl('/search')} />
<Redirect exact path={prefixUrl('/')} to={prefixUrl('/search')} />
<Route path={searchPath} component={SearchTracePage} />
<Route path={traceDiffPath} component={TraceDiff} />
<Route path={tracePath} component={TracePage} />
<Route path={dependenciesPath} component={DependencyGraph} />

<Redirect exact path="/" to={searchPath} />
<Redirect exact path={prefixUrl()} to={searchPath} />
<Redirect exact path={prefixUrl('/')} to={searchPath} />

<Route component={NotFound} />
</Switch>
</Page>
Expand Down
5 changes: 3 additions & 2 deletions packages/jaeger-ui/src/components/DependencyGraph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export const GRAPH_TYPES = {

const dagMaxNumServices = getConfigValue('dependencies.dagMaxNumServices') || FALLBACK_DAG_MAX_NUM_SERVICES;

export default class DependencyGraphPage extends Component {
// export for tests
export class DependencyGraphPageImpl extends Component {
static propTypes = {
// eslint-disable-next-line react/forbid-prop-types
dependencies: PropTypes.any.isRequired,
Expand Down Expand Up @@ -129,4 +130,4 @@ export function mapDispatchToProps(dispatch) {
return { fetchDependencies };
}

export const ConnectedDependencyGraphPage = connect(mapStateToProps, mapDispatchToProps)(DependencyGraphPage);
export default connect(mapStateToProps, mapDispatchToProps)(DependencyGraphPageImpl);
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ import { shallow } from 'enzyme';

import DAG from './DAG';
import DependencyForceGraph from './DependencyForceGraph';
import DependencyGraph, { GRAPH_TYPES, mapDispatchToProps, mapStateToProps } from './index';
import {
DependencyGraphPageImpl as DependencyGraph,
GRAPH_TYPES,
mapDispatchToProps,
mapStateToProps,
} from './index';
import LoadingIndicator from '../common/LoadingIndicator';

const childId = 'boomya';
Expand Down
Loading