From 5073839f8f3568e54864481b8d93986c729517dd Mon Sep 17 00:00:00 2001 From: Hamas Shafiq Date: Wed, 30 Nov 2022 18:09:08 +0000 Subject: [PATCH] Chore: Refactor span.js, trace.js & trace.fixture.js to TypeScript (#58006) --- .../selectors/{span.test.js => span.test.ts} | 40 ++--- .../src/selectors/{span.js => span.ts} | 46 +++--- .../{trace.fixture.js => trace.fixture.ts} | 18 ++- .../{trace.test.js => trace.test.ts} | 66 +++++---- .../src/selectors/{trace.js => trace.ts} | 138 ++++++++++-------- .../src/utils/TreeNode.ts | 6 +- 6 files changed, 175 insertions(+), 139 deletions(-) rename packages/jaeger-ui-components/src/selectors/{span.test.js => span.test.ts} (84%) rename packages/jaeger-ui-components/src/selectors/{span.js => span.ts} (56%) rename packages/jaeger-ui-components/src/selectors/{trace.fixture.js => trace.fixture.ts} (85%) rename packages/jaeger-ui-components/src/selectors/{trace.test.js => trace.test.ts} (88%) rename packages/jaeger-ui-components/src/selectors/{trace.js => trace.ts} (61%) diff --git a/packages/jaeger-ui-components/src/selectors/span.test.js b/packages/jaeger-ui-components/src/selectors/span.test.ts similarity index 84% rename from packages/jaeger-ui-components/src/selectors/span.test.js rename to packages/jaeger-ui-components/src/selectors/span.test.ts index 12893f81628..0771b01283a 100644 --- a/packages/jaeger-ui-components/src/selectors/span.test.js +++ b/packages/jaeger-ui-components/src/selectors/span.test.ts @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { TraceResponse } from 'src/types'; +import { TraceSpan, TraceSpanData } from 'src/types/trace'; + import traceGenerator from '../demo/trace-generators'; import * as spanSelectors from './span'; -const generatedTrace = traceGenerator.trace({ numberOfSpans: 45 }); +const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 }); it('getSpanId() should return the name of the span', () => { const span = generatedTrace.spans[0]; @@ -46,8 +49,10 @@ it('getSpanReferences() should return the span reference array', () => { expect(spanSelectors.getSpanReferences(generatedTrace.spans[0])).toEqual(generatedTrace.spans[0].references); }); -it('getSpanReferences() should return empty array for null references', () => { - expect(spanSelectors.getSpanReferences({ references: null })).toEqual([]); +it('getSpanReferences() should return an empty array when references is undefined', () => { + const span = generatedTrace.spans[0]; + span.references = undefined; + expect(spanSelectors.getSpanReferences(span)).toEqual([]); }); it('getSpanReferenceByType() should return the span reference requested', () => { @@ -55,7 +60,7 @@ it('getSpanReferenceByType() should return the span reference requested', () => spanSelectors.getSpanReferenceByType({ span: generatedTrace.spans[1], type: 'CHILD_OF', - }).refType + })?.refType ).toBe('CHILD_OF'); }); @@ -70,7 +75,7 @@ it('getSpanReferenceByType() should return undefined if one does not exist', () it('getSpanParentId() should return the spanID of the parent span', () => { expect(spanSelectors.getSpanParentId(generatedTrace.spans[1])).toBe( - generatedTrace.spans[1].references.find(({ refType }) => refType === 'CHILD_OF').spanID + generatedTrace.spans[1].references!.find(({ refType }: { refType: string }) => refType === 'CHILD_OF')!.spanID ); }); @@ -85,16 +90,17 @@ it('getSpanProcessId() should return the processID of the span', () => { }); it('getSpanProcess() should return the process of the span', () => { + const serviceName = 'bagel'; const span = { ...generatedTrace.spans[0], - process: {}, - }; + process: { serviceName }, + } as TraceSpan; expect(spanSelectors.getSpanProcess(span)).toBe(span.process); }); it('getSpanProcess() should throw if no process exists', () => { - expect(() => spanSelectors.getSpanProcess(generatedTrace.spans[0])).toThrow(); + expect(() => spanSelectors.getSpanProcess(generatedTrace.spans[0] as TraceSpan)).toThrow(); }); it('getSpanServiceName() should return the service name of the span', () => { @@ -102,7 +108,7 @@ it('getSpanServiceName() should return the service name of the span', () => { const span = { ...generatedTrace.spans[0], process: { serviceName }, - }; + } as TraceSpan; expect(spanSelectors.getSpanServiceName(span)).toBe(serviceName); }); @@ -112,17 +118,17 @@ it('filterSpansForTimestamps() should return a filtered list of spans between th const spans = [ { startTime: now - 1000, - id: 'start-time-1', + spanID: 'start-time-1', }, { startTime: now, - id: 'start-time-2', + spanID: 'start-time-2', }, { startTime: now + 1000, - id: 'start-time-3', + spanID: 'start-time-3', }, - ]; + ] as TraceSpanData[]; expect( spanSelectors.filterSpansForTimestamps({ @@ -164,23 +170,23 @@ it('filterSpansForText() should return a filtered list of spans between the time process: { serviceName: 'alpha', }, - id: 'start-time-1', + spanID: 'start-time-1', }, { operationName: 'GET /another', process: { serviceName: 'beta', }, - id: 'start-time-1', + spanID: 'start-time-1', }, { operationName: 'POST /mything', process: { serviceName: 'alpha', }, - id: 'start-time-1', + spanID: 'start-time-1', }, - ]; + ] as TraceSpan[]; expect( spanSelectors.filterSpansForText({ diff --git a/packages/jaeger-ui-components/src/selectors/span.js b/packages/jaeger-ui-components/src/selectors/span.ts similarity index 56% rename from packages/jaeger-ui-components/src/selectors/span.js rename to packages/jaeger-ui-components/src/selectors/span.ts index cd0a827dc4c..179dd1b8483 100644 --- a/packages/jaeger-ui-components/src/selectors/span.js +++ b/packages/jaeger-ui-components/src/selectors/span.ts @@ -15,25 +15,27 @@ import fuzzy from 'fuzzy'; import { createSelector } from 'reselect'; +import { TraceSpan, TraceSpanData, TraceSpanReference } from '../types/trace'; + import { getProcessServiceName } from './process'; -export const getSpanId = (span) => span.spanID; -export const getSpanName = (span) => span.operationName; -export const getSpanDuration = (span) => span.duration; -export const getSpanTimestamp = (span) => span.startTime; -export const getSpanProcessId = (span) => span.processID; -export const getSpanReferences = (span) => span.references || []; +export const getSpanId = (span: TraceSpanData) => span.spanID; +export const getSpanName = (span: TraceSpanData) => span.operationName; +export const getSpanDuration = (span: TraceSpanData) => span.duration; +export const getSpanTimestamp = (span: TraceSpanData) => span.startTime; +export const getSpanProcessId = (span: TraceSpanData) => span.processID; +export const getSpanReferences = (span: TraceSpanData) => span.references || []; export const getSpanReferenceByType = createSelector( - createSelector(({ span }) => span, getSpanReferences), - ({ type }) => type, - (references, type) => references.find((ref) => ref.refType === type) + createSelector(({ span }: { span: TraceSpanData }) => span, getSpanReferences), + ({ type }: { type: string }) => type, + (references, type) => references.find((ref: TraceSpanReference) => ref.refType === type) ); export const getSpanParentId = createSelector( - (span) => getSpanReferenceByType({ span, type: 'CHILD_OF' }), + (span: TraceSpanData) => getSpanReferenceByType({ span, type: 'CHILD_OF' }), (childOfRef) => (childOfRef ? childOfRef.spanID : null) ); -export const getSpanProcess = (span) => { +export const getSpanProcess = (span: TraceSpan) => { if (!span.process) { throw new Error( ` @@ -42,23 +44,22 @@ export const getSpanProcess = (span) => { ` ); } - return span.process; }; export const getSpanServiceName = createSelector(getSpanProcess, getProcessServiceName); export const filterSpansForTimestamps = createSelector( - ({ spans }) => spans, - ({ leftBound }) => leftBound, - ({ rightBound }) => rightBound, + ({ spans }: { spans: TraceSpanData[] }) => spans, + ({ leftBound }: { leftBound: number }) => leftBound, + ({ rightBound }: { rightBound: number }) => rightBound, (spans, leftBound, rightBound) => spans.filter((span) => getSpanTimestamp(span) >= leftBound && getSpanTimestamp(span) <= rightBound) ); export const filterSpansForText = createSelector( - ({ spans }) => spans, - ({ text }) => text, + ({ spans }: { spans: TraceSpan[] }) => spans, + ({ text }: { text: string }) => text, (spans, text) => fuzzy .filter(text, spans, { @@ -67,7 +68,7 @@ export const filterSpansForText = createSelector( .map(({ original }) => original) ); -const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpans) => +const getTextFilteredSpansAsMap = createSelector(filterSpansForText, (matchingSpans) => matchingSpans.reduce( (obj, span) => ({ ...obj, @@ -77,11 +78,12 @@ const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpa ) ); +// TODO: delete this function as it is not used? export const highlightSpansForTextFilter = createSelector( - ({ spans }) => spans, - getTextFilterdSpansAsMap, - (spans, textFilteredSpansMap) => - spans.map((span) => ({ + ({ spans }: { spans: TraceSpanData[] }) => spans, + getTextFilteredSpansAsMap, + (spans, textFilteredSpansMap: { [key: string]: TraceSpanData }) => + spans.map((span: TraceSpanData) => ({ ...span, muted: !textFilteredSpansMap[getSpanId(span)], })) diff --git a/packages/jaeger-ui-components/src/selectors/trace.fixture.js b/packages/jaeger-ui-components/src/selectors/trace.fixture.ts similarity index 85% rename from packages/jaeger-ui-components/src/selectors/trace.fixture.js rename to packages/jaeger-ui-components/src/selectors/trace.fixture.ts index 9c73a7a7bcb..6fdad979d6f 100644 --- a/packages/jaeger-ui-components/src/selectors/trace.fixture.js +++ b/packages/jaeger-ui-components/src/selectors/trace.fixture.ts @@ -14,6 +14,16 @@ // See https://github.com/jaegertracing/jaeger-ui/issues/115 for details. +import { TraceSpanReference } from '../types/trace'; + +const references: TraceSpanReference[] = [ + { + refType: 'FOLLOWS_FROM', + spanID: 'ea7cfaca83f0724b', + traceID: '2992f2a5b5d037a8aabffd08ef384237', + }, +]; + export const followsFromRef = { processes: { p1: { @@ -28,13 +38,7 @@ export const followsFromRef = { logs: [], operationName: 'thread', processID: 'p1', - references: [ - { - refType: 'FOLLOWS_FROM', - spanID: 'ea7cfaca83f0724b', - traceID: '2992f2a5b5d037a8aabffd08ef384237', - }, - ], + references: references, spanID: '1bdf4201221bb2ac', startTime: 1509533706521220, tags: [], diff --git a/packages/jaeger-ui-components/src/selectors/trace.test.js b/packages/jaeger-ui-components/src/selectors/trace.test.ts similarity index 88% rename from packages/jaeger-ui-components/src/selectors/trace.test.js rename to packages/jaeger-ui-components/src/selectors/trace.test.ts index 3e20f4d7060..dfaa9d28b93 100644 --- a/packages/jaeger-ui-components/src/selectors/trace.test.js +++ b/packages/jaeger-ui-components/src/selectors/trace.test.ts @@ -13,8 +13,10 @@ // limitations under the License. import { values as _values } from 'lodash'; +import TreeNode from 'src/utils/TreeNode'; import traceGenerator from '../demo/trace-generators'; +import { TraceResponse, TraceSpan, TraceSpanData } from '../types/trace'; import { numberSortComparator } from '../utils/sort'; import { @@ -29,7 +31,7 @@ import { import * as traceSelectors from './trace'; import { followsFromRef } from './trace.fixture'; -const generatedTrace = traceGenerator.trace({ numberOfSpans: 45 }); +const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 }); it('getTraceId() should return the traceID', () => { expect(traceSelectors.getTraceId(generatedTrace)).toBe(generatedTrace.traceID); @@ -39,7 +41,7 @@ it('hydrateSpansWithProcesses() should return the trace with processes on each s const hydratedTrace = traceSelectors.hydrateSpansWithProcesses(generatedTrace); hydratedTrace.spans.forEach((span) => - expect(getSpanProcess(span)).toBe(generatedTrace.processes[getSpanProcessId(span)]) + expect(getSpanProcess(span as TraceSpan)).toBe(generatedTrace.processes[getSpanProcessId(span)]) ); }); @@ -55,7 +57,7 @@ describe('getTraceSpanIdsAsTree()', () => { const tree = traceSelectors.getTraceSpanIdsAsTree(generatedTrace); const spanMap = traceSelectors.getTraceSpansAsMap(generatedTrace); - tree.walk((value, node) => { + tree.walk((value: string | number | undefined, node: TreeNode) => { const expectedParentValue = value === traceSelectors.TREE_ROOT_ID ? null : value; node.children.forEach((childNode) => { expect(getSpanParentId(spanMap.get(childNode.value))).toBe(expectedParentValue); @@ -98,7 +100,7 @@ it('getParentSpan() should return the first span if there are multiple parents', references: [], }, ], - }; + } as unknown as TraceResponse; expect(traceSelectors.getParentSpan(trace)).toBe(firstSpan); }); @@ -131,14 +133,14 @@ it('getTraceDepth() should determine the total depth of the trace tree', () => { }); it('getSpanDepthForTrace() should determine the depth of a given span in the parent', () => { - function testDepthCalc(span) { + function testDepthCalc(span: TraceSpanData) { let depth = 2; let currentId = getSpanParentId(span); - const findCurrentSpanById = (item) => getSpanId(item) === currentId; + const findCurrentSpanById = (item: TraceSpanData) => getSpanId(item) === currentId; while (currentId !== getSpanId(generatedTrace.spans[0])) { depth++; - currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById)); + currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById)!); } // console.log('hypothetical depth', depth); @@ -183,10 +185,13 @@ it('formatDurationForUnit() should use the formatters to return the proper value }); it('formatDurationForTrace() should return a ms value for traces shorter than a second', () => { + const firstSpan = generatedTrace.spans[0]; + firstSpan.duration = 600000; expect( traceSelectors.formatDurationForTrace({ trace: { - spans: [{ duration: 600000 }], + ...generatedTrace, + spans: [firstSpan], }, duration: 302000, }) @@ -266,14 +271,17 @@ it('getTreeSizeForTraceSpan() should return the size for a child span', () => { trace: generatedTrace, span: generatedTrace.spans[1], }) - ).toBe(traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(generatedTrace.spans[1].spanID).size - 1); + ).toBe(traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(generatedTrace.spans[1].spanID)!.size - 1); }); it('getTreeSizeForTraceSpan() should return -1 for an absent span', () => { + const absentSpan = generatedTrace.spans[0]; + absentSpan.spanID = 'whatever'; + expect( traceSelectors.getTreeSizeForTraceSpan({ trace: generatedTrace, - span: { spanID: 'whatever' }, + span: absentSpan, }) ).toBe(-1); }); @@ -287,7 +295,7 @@ it('getTraceName() should return the trace name based on the parentSpan', () => it('omitCollapsedSpans() should filter out collapsed spans', () => { const span = generatedTrace.spans[1]; - const size = traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(span.spanID).size - 1; + const size = traceSelectors.getTraceSpanIdsAsTree(generatedTrace).find(span.spanID)!.size - 1; expect( traceSelectors.omitCollapsedSpans({ @@ -299,15 +307,13 @@ it('omitCollapsedSpans() should filter out collapsed spans', () => { }); it('getTicksForTrace() should return a list of ticks given interval parameters', () => { + const trace = generatedTrace; const timestamp = new Date().getTime() * 1000; - const trace = { - spans: [ - { - startTime: timestamp, - duration: 3000000, - }, - ], - }; + + trace.spans.forEach((span) => { + span.duration = 3000000; + span.startTime = timestamp; + }); expect( traceSelectors.getTicksForTrace({ @@ -325,14 +331,20 @@ it('getTicksForTrace() should return a list of ticks given interval parameters', it('getTicksForTrace() should use defaults', () => { const timestamp = new Date().getTime() * 1000; - const trace = { - spans: [ - { - startTime: timestamp, - duration: 4000000, - }, - ], - }; + const trace = traceGenerator.trace({ numberOfSpans: 1 }); + + trace.spans = [ + { + traceID: '5031233a-d0b5-5d41-9b4b-4c072bcf5020', + processID: 'b5f4e0ff-7318-5017-a3f3-9c7b423a82aa', + spanID: 'e871771f-f1b4-54af-9e9d-826259c2915e', + flags: 0, + logs: [], + operationName: 'POST', + startTime: timestamp, + duration: 4000000, + }, + ]; expect(traceSelectors.getTicksForTrace({ trace })).toEqual([ { timestamp, width: traceSelectors.DEFAULT_TICK_WIDTH }, diff --git a/packages/jaeger-ui-components/src/selectors/trace.js b/packages/jaeger-ui-components/src/selectors/trace.ts similarity index 61% rename from packages/jaeger-ui-components/src/selectors/trace.js rename to packages/jaeger-ui-components/src/selectors/trace.ts index 35776aef0df..f9c0ebb85ca 100644 --- a/packages/jaeger-ui-components/src/selectors/trace.js +++ b/packages/jaeger-ui-components/src/selectors/trace.ts @@ -14,6 +14,7 @@ import { createSelector, createStructuredSelector } from 'reselect'; +import { Trace, TraceData, TraceProcess, TraceResponse, TraceSpan, TraceSpanData } from '../types/trace'; import TreeNode from '../utils/TreeNode'; import { formatMillisecondTime, formatSecondTime, ONE_SECOND } from '../utils/date'; import { numberSortComparator } from '../utils/sort'; @@ -28,15 +29,13 @@ import { getSpanProcessId, } from './span'; -export const getTraceId = (trace) => trace.traceID; - -export const getTraceSpans = (trace) => trace.spans; - -const getTraceProcesses = (trace) => trace.processes; +export const getTraceId = (trace: TraceData) => trace.traceID; +export const getTraceSpans = (trace: TraceResponse) => trace.spans; +const getTraceProcesses = (trace: TraceData | Trace) => trace.processes; const getSpanWithProcess = createSelector( - (state) => state.span, - (state) => state.processes, + (state: { span: TraceSpanData; processes: Record }) => state.span, + (state: { span: TraceSpanData; processes: Record }) => state.processes, (span, processes) => ({ ...span, process: processes[getSpanProcessId(span)], @@ -44,7 +43,7 @@ const getSpanWithProcess = createSelector( ); export const getTraceSpansAsMap = createSelector(getTraceSpans, (spans) => - spans.reduce((map, span) => map.set(getSpanId(span), span), new Map()) + spans.reduce((map, span: TraceSpanData) => map.set(getSpanId(span), span), new Map()) ); export const TREE_ROOT_ID = '__root__'; @@ -63,17 +62,17 @@ export const TREE_ROOT_ID = '__root__'; * @return {TreeNode} A tree of spanIDs derived from the relationships * between spans in the trace. */ -export function getTraceSpanIdsAsTree(trace) { - const nodesById = new Map(trace.spans.map((span) => [span.spanID, new TreeNode(span.spanID)])); - const spansById = new Map(trace.spans.map((span) => [span.spanID, span])); +export function getTraceSpanIdsAsTree(trace: TraceResponse) { + const nodesById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, new TreeNode(span.spanID)])); + const spansById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, span])); const root = new TreeNode(TREE_ROOT_ID); - trace.spans.forEach((span) => { - const node = nodesById.get(span.spanID); + trace.spans.forEach((span: TraceSpanData) => { + const node = nodesById.get(span.spanID)!; if (Array.isArray(span.references) && span.references.length) { const { refType, spanID: parentID } = span.references[0]; if (refType === 'CHILD_OF' || refType === 'FOLLOWS_FROM') { const parent = nodesById.get(parentID) || root; - parent.children.push(node); + parent.children?.push(node); } else { throw new Error(`Unrecognized ref type: ${refType}`); } @@ -81,15 +80,15 @@ export function getTraceSpanIdsAsTree(trace) { root.children.push(node); } }); - const comparator = (nodeA, nodeB) => { - const a = spansById.get(nodeA.value); - const b = spansById.get(nodeB.value); - return +(a.startTime > b.startTime) || +(a.startTime === b.startTime) - 1; + const comparator = (nodeA: TreeNode | undefined, nodeB: TreeNode | undefined) => { + const a: TraceSpanData | undefined = nodeA?.value ? spansById.get(nodeA.value.toString()) : undefined; + const b: TraceSpanData | undefined = nodeB?.value ? spansById.get(nodeB.value.toString()) : undefined; + return +(a?.startTime! > b?.startTime!) || +(a?.startTime === b?.startTime) - 1; }; - trace.spans.forEach((span) => { - const node = nodesById.get(span.spanID); - if (node.children.length > 1) { - node.children.sort(comparator); + trace.spans.forEach((span: TraceSpanData) => { + const node: TreeNode | undefined = nodesById.get(span.spanID); + if (node!.children.length > 1) { + node?.children.sort(comparator); } }); root.children.sort(comparator); @@ -97,13 +96,13 @@ export function getTraceSpanIdsAsTree(trace) { } // attach "process" as an object to each span. -export const hydrateSpansWithProcesses = (trace) => { +export const hydrateSpansWithProcesses = (trace: TraceResponse) => { const spans = getTraceSpans(trace); const processes = getTraceProcesses(trace); return { ...trace, - spans: spans.map((span) => getSpanWithProcess({ span, processes })), + spans: spans.map((span: TraceSpanData) => getSpanWithProcess({ span, processes })), }; }; @@ -111,25 +110,26 @@ export const getTraceSpanCount = createSelector(getTraceSpans, (spans) => spans. export const getTraceTimestamp = createSelector(getTraceSpans, (spans) => spans.reduce( - (prevTimestamp, span) => (prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span)), - null + (prevTimestamp: number, span: TraceSpanData) => + prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span), + 0 ) ); export const getTraceDuration = createSelector(getTraceSpans, getTraceTimestamp, (spans, timestamp) => spans.reduce( - (prevDuration, span) => + (prevDuration: number, span: TraceSpanData) => prevDuration - ? Math.max(getSpanTimestamp(span) - timestamp + getSpanDuration(span), prevDuration) + ? Math.max(getSpanTimestamp(span) - timestamp! + getSpanDuration(span), prevDuration) : getSpanDuration(span), - null + 0 ) ); export const getTraceEndTimestamp = createSelector( getTraceTimestamp, getTraceDuration, - (timestamp, duration) => timestamp + duration + (timestamp: number, duration: number) => timestamp! + duration ); export const getParentSpan = createSelector( @@ -137,16 +137,18 @@ export const getParentSpan = createSelector( getTraceSpansAsMap, (tree, spanMap) => tree.children - .map((node) => spanMap.get(node.value)) - .sort((spanA, spanB) => numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB)))[0] + .map((node: TreeNode) => spanMap.get(node.value)) + .sort((spanA: TraceSpanData, spanB: TraceSpanData) => + numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB)) + )[0] ); export const getTraceDepth = createSelector(getTraceSpanIdsAsTree, (spanTree) => spanTree.depth - 1); export const getSpanDepthForTrace = createSelector( - createSelector((state) => state.trace, getTraceSpanIdsAsTree), - createSelector((state) => state.span, getSpanId), - (node, spanID) => node.getPath(spanID).length - 1 + createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree), + createSelector((state: { span: TraceSpanData }) => state.span, getSpanId), + (node, spanID) => node.getPath(spanID)!.length - 1 ); export const getTraceServices = createSelector(getTraceProcesses, (processes) => @@ -165,26 +167,34 @@ export const DURATION_FORMATTERS = { s: formatSecondTime, }; -const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration) => +const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration: number) => totalDuration >= ONE_SECOND ? DURATION_FORMATTERS.s : DURATION_FORMATTERS.ms ); export const formatDurationForUnit = createSelector( - ({ duration }) => duration, - ({ unit }) => DURATION_FORMATTERS[unit], + ({ duration }: { duration: number }) => duration, + ({ unit }: { unit: 'ms' | 's' }) => DURATION_FORMATTERS[unit], (duration, formatter) => formatter(duration) ); export const formatDurationForTrace = createSelector( - ({ duration }) => duration, - createSelector(({ trace }) => trace, getDurationFormatterForTrace), + ({ duration }: { duration: number }) => duration, + createSelector(({ trace }: { trace: TraceResponse }) => trace, getDurationFormatterForTrace), (duration, formatter) => formatter(duration) ); export const getSortedSpans = createSelector( - ({ trace }) => trace, - ({ spans }) => spans, - ({ sort }) => sort, + ({ trace }: { trace: TraceResponse }) => trace, + ({ spans }: { spans: TraceSpanData[] }) => spans, + ({ + sort, + }: { + sort: { + dir: number; + comparator: (itemA: number, itemB: number) => number; + selector: (itemA: TraceSpanData, itemB: TraceResponse) => number; + }; + }) => sort, (trace, spans, { dir, comparator, selector }) => [...spans].sort((spanA, spanB) => dir * comparator(selector(spanA, trace), selector(spanB, trace))) ); @@ -192,13 +202,13 @@ export const getSortedSpans = createSelector( const getTraceSpansByHierarchyPosition = createSelector(getTraceSpanIdsAsTree, (tree) => { const hierarchyPositionMap = new Map(); let i = 0; - tree.walk((spanID) => hierarchyPositionMap.set(spanID, i++)); + tree.walk((spanID: string | number | undefined) => hierarchyPositionMap.set(spanID, i++)); return hierarchyPositionMap; }); export const getTreeSizeForTraceSpan = createSelector( - createSelector((state) => state.trace, getTraceSpanIdsAsTree), - createSelector((state) => state.span, getSpanId), + createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree), + createSelector((state: { span: TraceSpanData }) => state.span, getSpanId), (tree, spanID) => { const node = tree.find(spanID); if (!node) { @@ -209,8 +219,8 @@ export const getTreeSizeForTraceSpan = createSelector( ); export const getSpanHierarchySortPositionForTrace = createSelector( - createSelector(({ trace }) => trace, getTraceSpansByHierarchyPosition), - ({ span }) => span, + createSelector(({ trace }: { trace: Trace }) => trace, getTraceSpansByHierarchyPosition), + ({ span }: { span: TraceSpan }) => span, (hierarchyPositionMap, span) => hierarchyPositionMap.get(getSpanId(span)) ); @@ -222,16 +232,16 @@ export const getTraceName = createSelector( serviceName: getSpanServiceName, }) ), - ({ name, serviceName }) => `${serviceName}: ${name}` + ({ name, serviceName }: { name: string; serviceName: string }) => `${serviceName}: ${name}` ); export const omitCollapsedSpans = createSelector( - ({ spans }) => spans, - createSelector(({ trace }) => trace, getTraceSpanIdsAsTree), - ({ collapsed }) => collapsed, + ({ spans }: { spans: TraceSpanData[] }) => spans, + createSelector(({ trace }: { trace: TraceResponse }) => trace, getTraceSpanIdsAsTree), + ({ collapsed }: { collapsed: string[] }) => collapsed, (spans, tree, collapse) => { const hiddenSpanIds = collapse.reduce((result, collapsedSpanId) => { - tree.find(collapsedSpanId).walk((id) => id !== collapsedSpanId && result.add(id)); + tree.find(collapsedSpanId)!.walk((id: string | number | undefined) => id !== collapsedSpanId && result.add(id)); return result; }, new Set()); @@ -242,13 +252,13 @@ export const omitCollapsedSpans = createSelector( export const DEFAULT_TICK_INTERVAL = 4; export const DEFAULT_TICK_WIDTH = 3; export const getTicksForTrace = createSelector( - ({ trace }) => trace, - ({ interval = DEFAULT_TICK_INTERVAL }) => interval, - ({ width = DEFAULT_TICK_WIDTH }) => width, + ({ trace }: { trace: TraceResponse }) => trace, + ({ interval = DEFAULT_TICK_INTERVAL }: { interval?: number }) => interval, + ({ width = DEFAULT_TICK_WIDTH }: { width?: number }) => width, ( trace, - interval, - width + interval: number, + width: number // timestamps will be spaced over the interval, starting from the initial timestamp ) => [...Array(interval + 1).keys()].map((num) => ({ @@ -260,14 +270,16 @@ export const getTicksForTrace = createSelector( // TODO: delete this when the backend can ensure uniqueness /* istanbul ignore next */ export const enforceUniqueSpanIds = createSelector( - /* istanbul ignore next */ (trace) => trace, + /* istanbul ignore next */ (trace: Trace) => trace, getTraceSpans, /* istanbul ignore next */ (trace, spans) => { const map = new Map(); + const spanArray: TraceSpanData[] = []; + return { ...trace, - spans: spans.reduce((result, span) => { + spans: spans.reduce((result: TraceSpanData[], span: TraceSpanData) => { const spanID = map.has(getSpanId(span)) ? `${getSpanId(span)}_${map.get(getSpanId(span))}` : getSpanId(span); const updatedSpan = { ...span, spanID }; @@ -280,17 +292,17 @@ export const enforceUniqueSpanIds = createSelector( map.set(getSpanId(span), (map.get(getSpanId(span)) || 0) + 1); return result.concat([updatedSpan]); - }, []), + }, spanArray), }; } ); // TODO: delete this when the backend can ensure uniqueness export const dropEmptyStartTimeSpans = createSelector( - /* istanbul ignore next */ (trace) => trace, + /* istanbul ignore next */ (trace: Trace) => trace, getTraceSpans, /* istanbul ignore next */ (trace, spans) => ({ ...trace, - spans: spans.filter((span) => !!getSpanTimestamp(span)), + spans: spans.filter((span: TraceSpanData) => !!getSpanTimestamp(span)), }) ); diff --git a/packages/jaeger-ui-components/src/utils/TreeNode.ts b/packages/jaeger-ui-components/src/utils/TreeNode.ts index 8f8edeeea1e..168e1eec803 100644 --- a/packages/jaeger-ui-components/src/utils/TreeNode.ts +++ b/packages/jaeger-ui-components/src/utils/TreeNode.ts @@ -22,7 +22,7 @@ export default class TreeNode { return (node: TreeNode) => fn(node.value, node, depth); } - static searchFunction(search: TreeNode | number | SearchFn) { + static searchFunction(search: TreeNode | number | SearchFn | string) { if (typeof search === 'function') { return search; } @@ -51,7 +51,7 @@ export default class TreeNode { return this; } - find(search: TreeNode | number | SearchFn): TreeNode | null { + find(search: TreeNode | number | SearchFn | string): TreeNode | null { const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search)); if (searchFn(this)) { return this; @@ -65,7 +65,7 @@ export default class TreeNode { return null; } - getPath(search: TreeNode) { + getPath(search: TreeNode | string) { const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search)); const findPath = (currentNode: TreeNode, currentPath: TreeNode[]): TreeNode[] | null => {