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

Derive DDG from search results #445

Merged
merged 5 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type TProps = {
vertexKey: string;
};

// While browsers suport URLs of unlimited length, many server clients do not handle more than this max
// While browsers support URLs of unlimited length, many server clients do not handle more than this max
const MAX_LENGTH = 2083;
const MAX_LINKED_TRACES = 35;
const MIN_LENGTH = getSearchUrl().length;
Expand All @@ -55,13 +55,17 @@ export default class DdgNodeContent extends React.PureComponent<TProps> {
getVisiblePathElems: (vertexKey: string) => PathElem[] | undefined,
setViewModifier: (vertexKey: string, viewModifier: EViewModifier, enable: boolean) => void,
density: EDdgDensity,
showOp: boolean
showOp: boolean,
baseUrl: string,
extraUrlArgs: { [key: string]: unknown } | undefined
) {
return function renderNode(vertex: TDdgVertex, utils: TRendererUtils, lv: TLayoutVertex<any> | null) {
const { isFocalNode, key, operation, service } = vertex;
return (
<DdgNodeContent
focalNodeUrl={isFocalNode ? null : getUrl({ density, operation, service, showOp })}
focalNodeUrl={
isFocalNode ? null : getUrl({ density, operation, service, showOp, ...extraUrlArgs }, baseUrl)
}
getVisiblePathElems={getVisiblePathElems}
isFocalNode={isFocalNode}
isPositioned={Boolean(lv)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type TProps = {
uiFindMatches: Set<TDdgVertex> | undefined;
vertices: TDdgVertex[];
verticesViewModifiers: Map<string, number>;
baseUrl: string;
extraUrlArgs?: { [key: string]: unknown };
};

// The dichotomy between w/ & w/o VMs assumes that any edge VM neccesitates unmodified edges are de-emphasized
Expand Down Expand Up @@ -82,6 +84,8 @@ export default class Graph extends PureComponent<TProps> {
uiFindMatches,
vertices,
verticesViewModifiers,
baseUrl,
extraUrlArgs,
} = this.props;
const nodeRenderers = this.getNodeRenderers(uiFindMatches || this.emptyFindSet, verticesViewModifiers);

Expand Down Expand Up @@ -134,7 +138,14 @@ export default class Graph extends PureComponent<TProps> {
layerType: 'html',
measurable: true,
measureNode: DdgNodeContent.measureNode,
renderNode: this.getNodeContentRenderer(getVisiblePathElems, setViewModifier, density, showOp),
renderNode: this.getNodeContentRenderer(
getVisiblePathElems,
setViewModifier,
density,
showOp,
baseUrl,
extraUrlArgs
),
},
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type TProps = {
distanceToPathElems?: TDdgDistanceToPathElems;
handleClick: (distance: number, direction: EDirection) => void;
visEncoding?: string;
extraUrlArgs?: { [key: string]: unknown };
everett980 marked this conversation as resolved.
Show resolved Hide resolved
};

export default memo(function HopsSelector({ distanceToPathElems, handleClick, visEncoding }: TProps) {
Expand Down
45 changes: 28 additions & 17 deletions packages/jaeger-ui/src/components/DeepDependencies/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ type TProps = {
toggleShowOperations: (enable: boolean) => void;
uiFindCount: number | undefined;
visEncoding?: string;
showParameters?: boolean;
extraUrlArgs?: { [key: string]: unknown };
};
export default class Header extends React.PureComponent<TProps> {
private _uiFindInput: React.RefObject<Input> = React.createRef();

static defaultProps = {
showParameters: true,
};

focusUiFindInput = () => {
if (this._uiFindInput.current) {
this._uiFindInput.current.focus();
Expand Down Expand Up @@ -103,30 +109,34 @@ export default class Header extends React.PureComponent<TProps> {
showOperations,
toggleShowOperations,
visEncoding,
showParameters,
extraUrlArgs,
} = this.props;

return (
<header className="DdgHeader">
<div className="DdgHeader--paramsHeader">
<NameSelector
label="Service:"
placeholder="Select a service…"
value={service || null}
setValue={setService}
required
options={services || []}
/>
{service && (
{showParameters && (
<div className="DdgHeader--paramsHeader">
<NameSelector
label="Operation:"
placeholder="Select an operation…"
value={operation || null}
setValue={setOperation}
label="Service:"
placeholder="Select a service…"
value={service || null}
setValue={setService}
required
options={operations || []}
options={services || []}
/>
)}
</div>
{service && (
<NameSelector
label="Operation:"
placeholder="Select an operation…"
value={operation || null}
setValue={setOperation}
required
options={operations || []}
/>
)}
</div>
)}
<div className="DdgHeader--controlHeader">
<LayoutSettings
density={density}
Expand All @@ -138,6 +148,7 @@ export default class Header extends React.PureComponent<TProps> {
distanceToPathElems={distanceToPathElems}
handleClick={setDistance}
visEncoding={visEncoding}
extraUrlArgs={extraUrlArgs}
/>
<div className="DdgHeader--findWrapper">
<div className="DdgHeader--uiFind" role="button" onClick={this.focusUiFindInput}>
Expand Down
15 changes: 9 additions & 6 deletions packages/jaeger-ui/src/components/DeepDependencies/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('DeepDependencyGraphPage', () => {
const value = `new ${propName}`;
const kwarg = { [propName]: value };
ddgPageImpl.updateUrlState(kwarg);
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, props.urlState, kwarg));
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, props.urlState, kwarg), undefined);
expect(props.history.push).toHaveBeenCalledTimes(i + 1);
});
});
Expand All @@ -115,7 +115,7 @@ describe('DeepDependencyGraphPage', () => {
start: 'new start',
};
ddgPageImpl.updateUrlState(kwarg);
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, props.urlState, kwarg));
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, props.urlState, kwarg), undefined);
expect(props.history.push).toHaveBeenCalledTimes(1);
});

Expand All @@ -130,7 +130,7 @@ describe('DeepDependencyGraphPage', () => {
};
const ddgPageWithFewerProps = new DeepDependencyGraphPageImpl(otherProps);
ddgPageWithFewerProps.updateUrlState(kwarg);
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, otherUrlState, kwarg));
expect(getUrlSpy).toHaveBeenLastCalledWith(Object.assign({}, otherUrlState, kwarg), undefined);
expect(getUrlSpy).not.toHaveBeenLastCalledWith(expect.objectContaining({ start: expect.anything() }));
expect(props.history.push).toHaveBeenCalledTimes(1);
});
Expand Down Expand Up @@ -172,7 +172,8 @@ describe('DeepDependencyGraphPage', () => {
prevVisEncoding: visEncoding,
});
expect(getUrlSpy).toHaveBeenLastCalledWith(
Object.assign({}, props.urlState, { visEncoding: mockNewEncoding })
Object.assign({}, props.urlState, { visEncoding: mockNewEncoding }),
undefined
);
expect(props.history.push).toHaveBeenCalledTimes(1);
});
Expand All @@ -183,7 +184,8 @@ describe('DeepDependencyGraphPage', () => {
const operation = 'newOperation';
ddgPageImpl.setOperation(operation);
expect(getUrlSpy).toHaveBeenLastCalledWith(
Object.assign({}, props.urlState, { operation, visEncoding: undefined })
Object.assign({}, props.urlState, { operation, visEncoding: undefined }),
undefined
);
expect(props.history.push).toHaveBeenCalledTimes(1);
});
Expand All @@ -199,7 +201,8 @@ describe('DeepDependencyGraphPage', () => {
it('updates service and clears operation and visEncoding', () => {
ddgPageImpl.setService(service);
expect(getUrlSpy).toHaveBeenLastCalledWith(
Object.assign({}, props.urlState, { operation: undefined, service, visEncoding: undefined })
Object.assign({}, props.urlState, { operation: undefined, service, visEncoding: undefined }),
undefined
);
expect(props.history.push).toHaveBeenCalledTimes(1);
});
Expand Down
37 changes: 29 additions & 8 deletions packages/jaeger-ui/src/components/DeepDependencies/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { connect } from 'react-redux';

import Header from './Header';
import Graph from './Graph';
import { getUrl, getUrlState } from './url';
import { getUrl, getUrlState, ROUTE_PATH } from './url';
import ErrorMessage from '../common/ErrorMessage';
import LoadingIndicator from '../common/LoadingIndicator';
import { extractUiFindFromState, TExtractUiFindFromStateReturn } from '../common/UiFindInput';
Expand All @@ -43,7 +43,7 @@ import { TDdgStateEntry } from '../../types/TDdgState';

import './index.css';

type TDispatchProps = {
export type TDispatchProps = {
addViewModifier: (kwarg: TDdgModelParams & { viewModifier: number; visibilityIndices: number[] }) => void;
fetchDeepDependencyGraph: (query: TDdgModelParams) => void;
fetchServices: () => void;
Expand All @@ -53,23 +53,31 @@ type TDispatchProps = {
) => void;
};

type TReduxProps = TExtractUiFindFromStateReturn & {
export type TReduxProps = TExtractUiFindFromStateReturn & {
graph: GraphModel | undefined;
graphState?: TDdgStateEntry;
operationsForService: Record<string, string[]>;
services?: string[] | null;
urlState: TDdgSparseUrlState;
};

type TOwnProps = {
export type TOwnProps = {
history: RouterHistory;
location: Location;
showServicesOpsHeader: boolean;
baseUrl: string;
extraUrlArgs?: { [key: string]: unknown };
};

type TProps = TDispatchProps & TReduxProps & TOwnProps;
export type TProps = TDispatchProps & TReduxProps & TOwnProps;

// export for tests
export class DeepDependencyGraphPageImpl extends React.PureComponent<TProps> {
static defaultProps = {
showServicesOpsHeader: true,
baseUrl: ROUTE_PATH,
};

static fetchModelIfStale(props: TProps) {
const { fetchDeepDependencyGraph, graphState = null, urlState } = props;
const { service, operation } = urlState;
Expand Down Expand Up @@ -173,12 +181,22 @@ export class DeepDependencyGraphPageImpl extends React.PureComponent<TProps> {
toggleShowOperations = (enable: boolean) => this.updateUrlState({ showOp: enable });

updateUrlState = (newValues: Partial<TDdgSparseUrlState>) => {
const { uiFind, urlState, history } = this.props;
history.push(getUrl({ uiFind, ...urlState, ...newValues }));
const { uiFind, urlState, history, baseUrl, extraUrlArgs } = this.props;
history.push(getUrl({ uiFind, ...urlState, ...newValues, ...extraUrlArgs }, baseUrl));
};

render() {
const { graph, graphState, operationsForService, services, uiFind, urlState } = this.props;
const {
graph,
graphState,
operationsForService,
services,
uiFind,
urlState,
showServicesOpsHeader,
everett980 marked this conversation as resolved.
Show resolved Hide resolved
baseUrl,
extraUrlArgs,
} = this.props;
const { density, operation, service, showOp, visEncoding } = urlState;
const distanceToPathElems =
graphState && graphState.state === fetchedState.DONE ? graphState.model.distanceToPathElems : undefined;
Expand Down Expand Up @@ -208,6 +226,8 @@ export class DeepDependencyGraphPageImpl extends React.PureComponent<TProps> {
uiFindMatches={uiFindMatches}
vertices={vertices}
verticesViewModifiers={verticesViewModifiers}
baseUrl={baseUrl}
extraUrlArgs={extraUrlArgs}
/>
);
} else if (graphState.state === fetchedState.LOADING) {
Expand All @@ -227,6 +247,7 @@ export class DeepDependencyGraphPageImpl extends React.PureComponent<TProps> {
<div className="Ddg">
<div ref={this.headerWrapper}>
<Header
showParameters={showServicesOpsHeader}
density={density}
distanceToPathElems={distanceToPathElems}
hiddenUiFindMatches={hiddenUiFindMatches}
Expand Down
98 changes: 98 additions & 0 deletions packages/jaeger-ui/src/components/DeepDependencies/traces.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) 2019 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 queryString from 'query-string';
import { bindActionCreators, Dispatch } from 'redux';
import * as React from 'react';
import { connect } from 'react-redux';
import { ReduxState } from '../../types';
import * as jaegerApiActions from '../../actions/jaeger-api';
import ddgActions from '../../actions/ddg';
import { getUrlState } from './url';
import { TDdgStateEntry } from '../../types/TDdgState';
import GraphModel, { makeGraph } from '../../model/ddg/GraphModel';
import { fetchedState } from '../../constants';
import { extractUiFindFromState } from '../common/UiFindInput';
import transformDdgData from '../../model/ddg/transformDdgData';
import transformTracesToPaths from '../../model/ddg/transformTracesToPaths';
import { TDdgPayload } from '../../model/ddg/types';
import { ROUTE_PATH } from '../SearchTracePage/url';
import { DeepDependencyGraphPageImpl, TDispatchProps, TOwnProps, TProps, TReduxProps } from '.';

// export for tests
export function mapDispatchToProps(dispatch: Dispatch<ReduxState>): TDispatchProps {
const { fetchDeepDependencyGraph, fetchServiceOperations, fetchServices } = bindActionCreators(
jaegerApiActions,
dispatch
);
const { addViewModifier, removeViewModifierFromIndices } = bindActionCreators(ddgActions, dispatch);

return {
addViewModifier,
fetchDeepDependencyGraph,
fetchServiceOperations,
fetchServices,
removeViewModifierFromIndices,
};
}

export function mapStateToProps(state: ReduxState, ownProps: TOwnProps): TReduxProps {
const { trace } = state;
const urlState = getUrlState(ownProps.location.search);
const { density, operation, service, showOp } = urlState;
let graphState: TDdgStateEntry | undefined;
let graph: GraphModel | undefined;
let payload: TDdgPayload;
if (service) {
payload = transformTracesToPaths(trace.traces, service, operation);
graphState = {
model: transformDdgData(payload, { service, operation }),
state: fetchedState.DONE,
viewModifiers: new Map(),
};
graph = makeGraph(graphState.model, showOp, density);
}

return {
graph,
graphState,
services: undefined,
operationsForService: {},
urlState,
...extractUiFindFromState(state),
};
}

function tracesDDG() {
return class extends React.PureComponent<TProps & { showServicesOpsHeader: never; baseUrl: never }> {
everett980 marked this conversation as resolved.
Show resolved Hide resolved
render(): React.ReactNode {
const { location } = this.props;
const urlArgs = queryString.parse(location.search);
const { end, start, limit, lookback, maxDuration, minDuration, view } = urlArgs;
const extraArgs = { end, start, limit, lookback, maxDuration, minDuration, view };
return (
<DeepDependencyGraphPageImpl
showServicesOpsHeader={false}
baseUrl={ROUTE_PATH}
extraUrlArgs={extraArgs}
{...this.props}
/>
);
}
};
}

export default connect(
mapStateToProps,
mapDispatchToProps
)(tracesDDG());
Loading