diff --git a/packages/jaeger-ui/src/api/jaeger.js b/packages/jaeger-ui/src/api/jaeger.js index 0283e4179e..d4ec08d2c8 100644 --- a/packages/jaeger-ui/src/api/jaeger.js +++ b/packages/jaeger-ui/src/api/jaeger.js @@ -76,6 +76,83 @@ function getJSON(url, options = {}) { }); } +function populateSpanReference(reference, traceUrl) { + if (reference.span) { + return Promise.resolve(); + } + + return ( + getJSON(traceUrl) + .then(refTraceData => { + const referenceTrace = refTraceData && refTraceData.data && refTraceData.data[0]; + if ( + !referenceTrace || + !Array.isArray(referenceTrace.spans) || + typeof referenceTrace.processes !== 'object' + ) { + return; + } + + // We need to reassign `reference.span`, hence ignore no-param-reassign linting rule. + // eslint-disable-next-line + reference.span = referenceTrace.spans.find( + candidateSpan => candidateSpan.spanID === reference.spanID + ); + if (reference.span && typeof reference.span.processID === 'string') { + // We need to reassign `reference.span.process`, hence ignore no-param-reassign linting rule. + // eslint-disable-next-line + reference.span.process = referenceTrace.processes[reference.span.processID]; + } + }) + // Catch and swallow error. If we can't parse the reference, Jaeger UI should continue to function + // without `reference.span` population. + .catch(e => { + // eslint-disable-next-line + console.error(e); + }) + ); +} + +function populateSpanReferences(traceData, traceUrl) { + if (!traceData || !Array.isArray(traceData.data)) { + return traceData; + } + + const populateSpanReferencesPromises = []; + try { + traceData.data.map(trace => { + if (trace && Array.isArray(trace.spans)) { + trace.spans.map(span => { + if (!Array.isArray(span.references)) { + return null; + } + + span.references.map(reference => { + if (reference && reference.refType === 'FOLLOWS_FROM' && reference.traceID !== trace.traceID) { + populateSpanReferencesPromises.push( + populateSpanReference(reference, `${traceUrl}${reference.traceID}`) + ); + } + + return null; + }); + return null; + }); + } + return null; + }); + } catch (e) { + // Catch and swallow error. If we can't parse the reference, Jaeger UI should continue to function + // `reference.span` population. + + // eslint-disable-next-line + console.error(e); + return Promise.resolve(traceData); + } + + return Promise.all(populateSpanReferencesPromises).then(() => traceData); +} + export const DEFAULT_API_ROOT = prefixUrl('/api/'); export const ANALYTICS_ROOT = prefixUrl('/analytics/'); export const DEFAULT_DEPENDENCY_LOOKBACK = moment.duration(1, 'weeks').asMilliseconds(); @@ -112,10 +189,14 @@ const JaegerAPI = { return getJSON(`${this.apiRoot}services`); }, fetchTrace(id) { - return getJSON(`${this.apiRoot}traces/${id}`); + return getJSON(`${this.apiRoot}traces/${id}`).then(traceData => + populateSpanReferences(traceData, `${this.apiRoot}traces/`) + ); }, searchTraces(query) { - return getJSON(`${this.apiRoot}traces`, { query }); + return getJSON(`${this.apiRoot}traces`, { query }).then(traceData => + populateSpanReferences(traceData, `${this.apiRoot}traces/`) + ); }, fetchMetrics(metricType, serviceNameList, query) { const servicesName = serviceNameList.map(serviceName => `service=${serviceName}`).join(',');