Commit 28383bcf by David Committed by GitHub

TraceViewer: Make sure it does not break when no trace is passed (#28909)

* TraceViewer: Make sure it does not break when no trace is passed

- exit trace conversion early when there is no trace data
- added test to cover missing trace for trace viewer

* Review feedback

* Use RTL for new test

* Exit early if trace or traceID is not there
parent 145901c0
...@@ -80,6 +80,10 @@ export type TraceData = { ...@@ -80,6 +80,10 @@ export type TraceData = {
warnings?: string[] | null; warnings?: string[] | null;
}; };
export type TraceViewData = TraceData & {
spans: TraceSpanData[];
};
export type Trace = TraceData & { export type Trace = TraceData & {
duration: number; duration: number;
endTime: number; endTime: number;
......
...@@ -17,7 +17,7 @@ import _isEqual from 'lodash/isEqual'; ...@@ -17,7 +17,7 @@ import _isEqual from 'lodash/isEqual';
// @ts-ignore // @ts-ignore
import { getTraceSpanIdsAsTree } from '../selectors/trace'; import { getTraceSpanIdsAsTree } from '../selectors/trace';
import { getConfigValue } from '../utils/config/get-config'; import { getConfigValue } from '../utils/config/get-config';
import { TraceKeyValuePair, TraceSpan, TraceSpanData, Trace, TraceData } from '@grafana/data'; import { TraceKeyValuePair, TraceSpan, Trace, TraceViewData } from '@grafana/data';
// @ts-ignore // @ts-ignore
import TreeNode from '../utils/TreeNode'; import TreeNode from '../utils/TreeNode';
...@@ -71,12 +71,11 @@ export function orderTags(spanTags: TraceKeyValuePair[], topPrefixes?: string[]) ...@@ -71,12 +71,11 @@ export function orderTags(spanTags: TraceKeyValuePair[], topPrefixes?: string[])
* NOTE: Mutates `data` - Transform the HTTP response data into the form the app * NOTE: Mutates `data` - Transform the HTTP response data into the form the app
* generally requires. * generally requires.
*/ */
export default function transformTraceData(data: TraceData & { spans: TraceSpanData[] }): Trace | null { export default function transformTraceData(data: TraceViewData | undefined): Trace | null {
let { traceID } = data; if (!data?.traceID) {
if (!traceID) {
return null; return null;
} }
traceID = traceID.toLowerCase(); const traceID = data.traceID.toLowerCase();
let traceEndTime = 0; let traceEndTime = 0;
let traceStartTime = Number.MAX_SAFE_INTEGER; let traceStartTime = Number.MAX_SAFE_INTEGER;
......
...@@ -22,6 +22,7 @@ import { ...@@ -22,6 +22,7 @@ import {
LogsModel, LogsModel,
EventBusExtended, EventBusExtended,
EventBusSrv, EventBusSrv,
TraceViewData,
} from '@grafana/data'; } from '@grafana/data';
import store from 'app/core/store'; import store from 'app/core/store';
...@@ -401,7 +402,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> { ...@@ -401,7 +402,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
// If there is not data (like 404) we show a separate error so no need to show anything here // If there is not data (like 404) we show a separate error so no need to show anything here
queryResponse.series[0] && ( queryResponse.series[0] && (
<TraceView <TraceView
trace={queryResponse.series[0].fields[0].values.get(0) as any} trace={queryResponse.series[0].fields[0].values.get(0) as TraceViewData | undefined}
splitOpenFn={splitOpen} splitOpenFn={splitOpen}
/> />
)} )}
......
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { render } from '@testing-library/react';
import { TraceView } from './TraceView'; import { TraceView } from './TraceView';
import { TracePageHeader, TraceTimelineViewer } from '@jaegertracing/jaeger-ui-components'; import { TracePageHeader, TraceTimelineViewer } from '@jaegertracing/jaeger-ui-components';
import { TraceSpanData, TraceData } from '@grafana/data'; import { TraceSpanData, TraceData } from '@grafana/data';
...@@ -20,6 +21,13 @@ describe('TraceView', () => { ...@@ -20,6 +21,13 @@ describe('TraceView', () => {
expect(header).toHaveLength(1); expect(header).toHaveLength(1);
}); });
it('does not render anything on missing trace', () => {
// Simulating Explore's access to empty response data
const trace = [][0];
const { container } = render(<TraceView trace={trace} splitOpenFn={() => {}} />);
expect(container.hasChildNodes()).toBeFalsy();
});
it('toggles detailState', () => { it('toggles detailState', () => {
let { timeline, wrapper } = renderTraceView(); let { timeline, wrapper } = renderTraceView();
expect(timeline.props().traceTimeline.detailStates.size).toBe(0); expect(timeline.props().traceTimeline.detailStates.size).toBe(0);
......
...@@ -16,15 +16,18 @@ import { useChildrenState } from './useChildrenState'; ...@@ -16,15 +16,18 @@ import { useChildrenState } from './useChildrenState';
import { useDetailState } from './useDetailState'; import { useDetailState } from './useDetailState';
import { useHoverIndentGuide } from './useHoverIndentGuide'; import { useHoverIndentGuide } from './useHoverIndentGuide';
import { colors, useTheme } from '@grafana/ui'; import { colors, useTheme } from '@grafana/ui';
import { TraceData, TraceSpanData, Trace, TraceSpan, TraceKeyValuePair, TraceLink } from '@grafana/data'; import { TraceViewData, Trace, TraceSpan, TraceKeyValuePair, TraceLink } from '@grafana/data';
import { createSpanLinkFactory } from './createSpanLink'; import { createSpanLinkFactory } from './createSpanLink';
type Props = { type Props = {
trace: TraceData & { spans: TraceSpanData[] }; trace?: TraceViewData;
splitOpenFn: (options: { datasourceUid: string; query: any }) => void; splitOpenFn: (options: { datasourceUid: string; query: any }) => void;
}; };
export function TraceView(props: Props) { export function TraceView(props: Props) {
if (!props.trace?.traceID) {
return null;
}
const { expandOne, collapseOne, childrenToggle, collapseAll, childrenHiddenIDs, expandAll } = useChildrenState(); const { expandOne, collapseOne, childrenToggle, collapseAll, childrenHiddenIDs, expandAll } = useChildrenState();
const { const {
detailStates, detailStates,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment