mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Refactor span.js, trace.js & trace.fixture.js to TypeScript (#58006)
This commit is contained in:
parent
ce630b2dc5
commit
5073839f8f
@ -12,11 +12,14 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { TraceResponse } from 'src/types';
|
||||||
|
import { TraceSpan, TraceSpanData } from 'src/types/trace';
|
||||||
|
|
||||||
import traceGenerator from '../demo/trace-generators';
|
import traceGenerator from '../demo/trace-generators';
|
||||||
|
|
||||||
import * as spanSelectors from './span';
|
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', () => {
|
it('getSpanId() should return the name of the span', () => {
|
||||||
const span = generatedTrace.spans[0];
|
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);
|
expect(spanSelectors.getSpanReferences(generatedTrace.spans[0])).toEqual(generatedTrace.spans[0].references);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSpanReferences() should return empty array for null references', () => {
|
it('getSpanReferences() should return an empty array when references is undefined', () => {
|
||||||
expect(spanSelectors.getSpanReferences({ references: null })).toEqual([]);
|
const span = generatedTrace.spans[0];
|
||||||
|
span.references = undefined;
|
||||||
|
expect(spanSelectors.getSpanReferences(span)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSpanReferenceByType() should return the span reference requested', () => {
|
it('getSpanReferenceByType() should return the span reference requested', () => {
|
||||||
@ -55,7 +60,7 @@ it('getSpanReferenceByType() should return the span reference requested', () =>
|
|||||||
spanSelectors.getSpanReferenceByType({
|
spanSelectors.getSpanReferenceByType({
|
||||||
span: generatedTrace.spans[1],
|
span: generatedTrace.spans[1],
|
||||||
type: 'CHILD_OF',
|
type: 'CHILD_OF',
|
||||||
}).refType
|
})?.refType
|
||||||
).toBe('CHILD_OF');
|
).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', () => {
|
it('getSpanParentId() should return the spanID of the parent span', () => {
|
||||||
expect(spanSelectors.getSpanParentId(generatedTrace.spans[1])).toBe(
|
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', () => {
|
it('getSpanProcess() should return the process of the span', () => {
|
||||||
|
const serviceName = 'bagel';
|
||||||
const span = {
|
const span = {
|
||||||
...generatedTrace.spans[0],
|
...generatedTrace.spans[0],
|
||||||
process: {},
|
process: { serviceName },
|
||||||
};
|
} as TraceSpan;
|
||||||
|
|
||||||
expect(spanSelectors.getSpanProcess(span)).toBe(span.process);
|
expect(spanSelectors.getSpanProcess(span)).toBe(span.process);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getSpanProcess() should throw if no process exists', () => {
|
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', () => {
|
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 = {
|
const span = {
|
||||||
...generatedTrace.spans[0],
|
...generatedTrace.spans[0],
|
||||||
process: { serviceName },
|
process: { serviceName },
|
||||||
};
|
} as TraceSpan;
|
||||||
|
|
||||||
expect(spanSelectors.getSpanServiceName(span)).toBe(serviceName);
|
expect(spanSelectors.getSpanServiceName(span)).toBe(serviceName);
|
||||||
});
|
});
|
||||||
@ -112,17 +118,17 @@ it('filterSpansForTimestamps() should return a filtered list of spans between th
|
|||||||
const spans = [
|
const spans = [
|
||||||
{
|
{
|
||||||
startTime: now - 1000,
|
startTime: now - 1000,
|
||||||
id: 'start-time-1',
|
spanID: 'start-time-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
startTime: now,
|
startTime: now,
|
||||||
id: 'start-time-2',
|
spanID: 'start-time-2',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
startTime: now + 1000,
|
startTime: now + 1000,
|
||||||
id: 'start-time-3',
|
spanID: 'start-time-3',
|
||||||
},
|
},
|
||||||
];
|
] as TraceSpanData[];
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
spanSelectors.filterSpansForTimestamps({
|
spanSelectors.filterSpansForTimestamps({
|
||||||
@ -164,23 +170,23 @@ it('filterSpansForText() should return a filtered list of spans between the time
|
|||||||
process: {
|
process: {
|
||||||
serviceName: 'alpha',
|
serviceName: 'alpha',
|
||||||
},
|
},
|
||||||
id: 'start-time-1',
|
spanID: 'start-time-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
operationName: 'GET /another',
|
operationName: 'GET /another',
|
||||||
process: {
|
process: {
|
||||||
serviceName: 'beta',
|
serviceName: 'beta',
|
||||||
},
|
},
|
||||||
id: 'start-time-1',
|
spanID: 'start-time-1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
operationName: 'POST /mything',
|
operationName: 'POST /mything',
|
||||||
process: {
|
process: {
|
||||||
serviceName: 'alpha',
|
serviceName: 'alpha',
|
||||||
},
|
},
|
||||||
id: 'start-time-1',
|
spanID: 'start-time-1',
|
||||||
},
|
},
|
||||||
];
|
] as TraceSpan[];
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
spanSelectors.filterSpansForText({
|
spanSelectors.filterSpansForText({
|
@ -15,25 +15,27 @@
|
|||||||
import fuzzy from 'fuzzy';
|
import fuzzy from 'fuzzy';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
import { TraceSpan, TraceSpanData, TraceSpanReference } from '../types/trace';
|
||||||
|
|
||||||
import { getProcessServiceName } from './process';
|
import { getProcessServiceName } from './process';
|
||||||
|
|
||||||
export const getSpanId = (span) => span.spanID;
|
export const getSpanId = (span: TraceSpanData) => span.spanID;
|
||||||
export const getSpanName = (span) => span.operationName;
|
export const getSpanName = (span: TraceSpanData) => span.operationName;
|
||||||
export const getSpanDuration = (span) => span.duration;
|
export const getSpanDuration = (span: TraceSpanData) => span.duration;
|
||||||
export const getSpanTimestamp = (span) => span.startTime;
|
export const getSpanTimestamp = (span: TraceSpanData) => span.startTime;
|
||||||
export const getSpanProcessId = (span) => span.processID;
|
export const getSpanProcessId = (span: TraceSpanData) => span.processID;
|
||||||
export const getSpanReferences = (span) => span.references || [];
|
export const getSpanReferences = (span: TraceSpanData) => span.references || [];
|
||||||
export const getSpanReferenceByType = createSelector(
|
export const getSpanReferenceByType = createSelector(
|
||||||
createSelector(({ span }) => span, getSpanReferences),
|
createSelector(({ span }: { span: TraceSpanData }) => span, getSpanReferences),
|
||||||
({ type }) => type,
|
({ type }: { type: string }) => type,
|
||||||
(references, type) => references.find((ref) => ref.refType === type)
|
(references, type) => references.find((ref: TraceSpanReference) => ref.refType === type)
|
||||||
);
|
);
|
||||||
export const getSpanParentId = createSelector(
|
export const getSpanParentId = createSelector(
|
||||||
(span) => getSpanReferenceByType({ span, type: 'CHILD_OF' }),
|
(span: TraceSpanData) => getSpanReferenceByType({ span, type: 'CHILD_OF' }),
|
||||||
(childOfRef) => (childOfRef ? childOfRef.spanID : null)
|
(childOfRef) => (childOfRef ? childOfRef.spanID : null)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getSpanProcess = (span) => {
|
export const getSpanProcess = (span: TraceSpan) => {
|
||||||
if (!span.process) {
|
if (!span.process) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`
|
`
|
||||||
@ -42,23 +44,22 @@ export const getSpanProcess = (span) => {
|
|||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return span.process;
|
return span.process;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSpanServiceName = createSelector(getSpanProcess, getProcessServiceName);
|
export const getSpanServiceName = createSelector(getSpanProcess, getProcessServiceName);
|
||||||
|
|
||||||
export const filterSpansForTimestamps = createSelector(
|
export const filterSpansForTimestamps = createSelector(
|
||||||
({ spans }) => spans,
|
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||||
({ leftBound }) => leftBound,
|
({ leftBound }: { leftBound: number }) => leftBound,
|
||||||
({ rightBound }) => rightBound,
|
({ rightBound }: { rightBound: number }) => rightBound,
|
||||||
(spans, leftBound, rightBound) =>
|
(spans, leftBound, rightBound) =>
|
||||||
spans.filter((span) => getSpanTimestamp(span) >= leftBound && getSpanTimestamp(span) <= rightBound)
|
spans.filter((span) => getSpanTimestamp(span) >= leftBound && getSpanTimestamp(span) <= rightBound)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const filterSpansForText = createSelector(
|
export const filterSpansForText = createSelector(
|
||||||
({ spans }) => spans,
|
({ spans }: { spans: TraceSpan[] }) => spans,
|
||||||
({ text }) => text,
|
({ text }: { text: string }) => text,
|
||||||
(spans, text) =>
|
(spans, text) =>
|
||||||
fuzzy
|
fuzzy
|
||||||
.filter(text, spans, {
|
.filter(text, spans, {
|
||||||
@ -67,7 +68,7 @@ export const filterSpansForText = createSelector(
|
|||||||
.map(({ original }) => original)
|
.map(({ original }) => original)
|
||||||
);
|
);
|
||||||
|
|
||||||
const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpans) =>
|
const getTextFilteredSpansAsMap = createSelector(filterSpansForText, (matchingSpans) =>
|
||||||
matchingSpans.reduce(
|
matchingSpans.reduce(
|
||||||
(obj, span) => ({
|
(obj, span) => ({
|
||||||
...obj,
|
...obj,
|
||||||
@ -77,11 +78,12 @@ const getTextFilterdSpansAsMap = createSelector(filterSpansForText, (matchingSpa
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO: delete this function as it is not used?
|
||||||
export const highlightSpansForTextFilter = createSelector(
|
export const highlightSpansForTextFilter = createSelector(
|
||||||
({ spans }) => spans,
|
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||||
getTextFilterdSpansAsMap,
|
getTextFilteredSpansAsMap,
|
||||||
(spans, textFilteredSpansMap) =>
|
(spans, textFilteredSpansMap: { [key: string]: TraceSpanData }) =>
|
||||||
spans.map((span) => ({
|
spans.map((span: TraceSpanData) => ({
|
||||||
...span,
|
...span,
|
||||||
muted: !textFilteredSpansMap[getSpanId(span)],
|
muted: !textFilteredSpansMap[getSpanId(span)],
|
||||||
}))
|
}))
|
@ -14,6 +14,16 @@
|
|||||||
|
|
||||||
// See https://github.com/jaegertracing/jaeger-ui/issues/115 for details.
|
// 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 = {
|
export const followsFromRef = {
|
||||||
processes: {
|
processes: {
|
||||||
p1: {
|
p1: {
|
||||||
@ -28,13 +38,7 @@ export const followsFromRef = {
|
|||||||
logs: [],
|
logs: [],
|
||||||
operationName: 'thread',
|
operationName: 'thread',
|
||||||
processID: 'p1',
|
processID: 'p1',
|
||||||
references: [
|
references: references,
|
||||||
{
|
|
||||||
refType: 'FOLLOWS_FROM',
|
|
||||||
spanID: 'ea7cfaca83f0724b',
|
|
||||||
traceID: '2992f2a5b5d037a8aabffd08ef384237',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
spanID: '1bdf4201221bb2ac',
|
spanID: '1bdf4201221bb2ac',
|
||||||
startTime: 1509533706521220,
|
startTime: 1509533706521220,
|
||||||
tags: [],
|
tags: [],
|
@ -13,8 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { values as _values } from 'lodash';
|
import { values as _values } from 'lodash';
|
||||||
|
import TreeNode from 'src/utils/TreeNode';
|
||||||
|
|
||||||
import traceGenerator from '../demo/trace-generators';
|
import traceGenerator from '../demo/trace-generators';
|
||||||
|
import { TraceResponse, TraceSpan, TraceSpanData } from '../types/trace';
|
||||||
import { numberSortComparator } from '../utils/sort';
|
import { numberSortComparator } from '../utils/sort';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -29,7 +31,7 @@ import {
|
|||||||
import * as traceSelectors from './trace';
|
import * as traceSelectors from './trace';
|
||||||
import { followsFromRef } from './trace.fixture';
|
import { followsFromRef } from './trace.fixture';
|
||||||
|
|
||||||
const generatedTrace = traceGenerator.trace({ numberOfSpans: 45 });
|
const generatedTrace: TraceResponse = traceGenerator.trace({ numberOfSpans: 45 });
|
||||||
|
|
||||||
it('getTraceId() should return the traceID', () => {
|
it('getTraceId() should return the traceID', () => {
|
||||||
expect(traceSelectors.getTraceId(generatedTrace)).toBe(generatedTrace.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);
|
const hydratedTrace = traceSelectors.hydrateSpansWithProcesses(generatedTrace);
|
||||||
|
|
||||||
hydratedTrace.spans.forEach((span) =>
|
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 tree = traceSelectors.getTraceSpanIdsAsTree(generatedTrace);
|
||||||
const spanMap = traceSelectors.getTraceSpansAsMap(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;
|
const expectedParentValue = value === traceSelectors.TREE_ROOT_ID ? null : value;
|
||||||
node.children.forEach((childNode) => {
|
node.children.forEach((childNode) => {
|
||||||
expect(getSpanParentId(spanMap.get(childNode.value))).toBe(expectedParentValue);
|
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: [],
|
references: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
} as unknown as TraceResponse;
|
||||||
|
|
||||||
expect(traceSelectors.getParentSpan(trace)).toBe(firstSpan);
|
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', () => {
|
it('getSpanDepthForTrace() should determine the depth of a given span in the parent', () => {
|
||||||
function testDepthCalc(span) {
|
function testDepthCalc(span: TraceSpanData) {
|
||||||
let depth = 2;
|
let depth = 2;
|
||||||
let currentId = getSpanParentId(span);
|
let currentId = getSpanParentId(span);
|
||||||
|
|
||||||
const findCurrentSpanById = (item) => getSpanId(item) === currentId;
|
const findCurrentSpanById = (item: TraceSpanData) => getSpanId(item) === currentId;
|
||||||
while (currentId !== getSpanId(generatedTrace.spans[0])) {
|
while (currentId !== getSpanId(generatedTrace.spans[0])) {
|
||||||
depth++;
|
depth++;
|
||||||
currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById));
|
currentId = getSpanParentId(generatedTrace.spans.find(findCurrentSpanById)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('hypothetical depth', depth);
|
// 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', () => {
|
it('formatDurationForTrace() should return a ms value for traces shorter than a second', () => {
|
||||||
|
const firstSpan = generatedTrace.spans[0];
|
||||||
|
firstSpan.duration = 600000;
|
||||||
expect(
|
expect(
|
||||||
traceSelectors.formatDurationForTrace({
|
traceSelectors.formatDurationForTrace({
|
||||||
trace: {
|
trace: {
|
||||||
spans: [{ duration: 600000 }],
|
...generatedTrace,
|
||||||
|
spans: [firstSpan],
|
||||||
},
|
},
|
||||||
duration: 302000,
|
duration: 302000,
|
||||||
})
|
})
|
||||||
@ -266,14 +271,17 @@ it('getTreeSizeForTraceSpan() should return the size for a child span', () => {
|
|||||||
trace: generatedTrace,
|
trace: generatedTrace,
|
||||||
span: generatedTrace.spans[1],
|
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', () => {
|
it('getTreeSizeForTraceSpan() should return -1 for an absent span', () => {
|
||||||
|
const absentSpan = generatedTrace.spans[0];
|
||||||
|
absentSpan.spanID = 'whatever';
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
traceSelectors.getTreeSizeForTraceSpan({
|
traceSelectors.getTreeSizeForTraceSpan({
|
||||||
trace: generatedTrace,
|
trace: generatedTrace,
|
||||||
span: { spanID: 'whatever' },
|
span: absentSpan,
|
||||||
})
|
})
|
||||||
).toBe(-1);
|
).toBe(-1);
|
||||||
});
|
});
|
||||||
@ -287,7 +295,7 @@ it('getTraceName() should return the trace name based on the parentSpan', () =>
|
|||||||
|
|
||||||
it('omitCollapsedSpans() should filter out collapsed spans', () => {
|
it('omitCollapsedSpans() should filter out collapsed spans', () => {
|
||||||
const span = generatedTrace.spans[1];
|
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(
|
expect(
|
||||||
traceSelectors.omitCollapsedSpans({
|
traceSelectors.omitCollapsedSpans({
|
||||||
@ -299,15 +307,13 @@ it('omitCollapsedSpans() should filter out collapsed spans', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getTicksForTrace() should return a list of ticks given interval parameters', () => {
|
it('getTicksForTrace() should return a list of ticks given interval parameters', () => {
|
||||||
|
const trace = generatedTrace;
|
||||||
const timestamp = new Date().getTime() * 1000;
|
const timestamp = new Date().getTime() * 1000;
|
||||||
const trace = {
|
|
||||||
spans: [
|
trace.spans.forEach((span) => {
|
||||||
{
|
span.duration = 3000000;
|
||||||
startTime: timestamp,
|
span.startTime = timestamp;
|
||||||
duration: 3000000,
|
});
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
traceSelectors.getTicksForTrace({
|
traceSelectors.getTicksForTrace({
|
||||||
@ -325,14 +331,20 @@ it('getTicksForTrace() should return a list of ticks given interval parameters',
|
|||||||
|
|
||||||
it('getTicksForTrace() should use defaults', () => {
|
it('getTicksForTrace() should use defaults', () => {
|
||||||
const timestamp = new Date().getTime() * 1000;
|
const timestamp = new Date().getTime() * 1000;
|
||||||
const trace = {
|
const trace = traceGenerator.trace({ numberOfSpans: 1 });
|
||||||
spans: [
|
|
||||||
{
|
trace.spans = [
|
||||||
startTime: timestamp,
|
{
|
||||||
duration: 4000000,
|
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([
|
expect(traceSelectors.getTicksForTrace({ trace })).toEqual([
|
||||||
{ timestamp, width: traceSelectors.DEFAULT_TICK_WIDTH },
|
{ timestamp, width: traceSelectors.DEFAULT_TICK_WIDTH },
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import { createSelector, createStructuredSelector } from 'reselect';
|
import { createSelector, createStructuredSelector } from 'reselect';
|
||||||
|
|
||||||
|
import { Trace, TraceData, TraceProcess, TraceResponse, TraceSpan, TraceSpanData } from '../types/trace';
|
||||||
import TreeNode from '../utils/TreeNode';
|
import TreeNode from '../utils/TreeNode';
|
||||||
import { formatMillisecondTime, formatSecondTime, ONE_SECOND } from '../utils/date';
|
import { formatMillisecondTime, formatSecondTime, ONE_SECOND } from '../utils/date';
|
||||||
import { numberSortComparator } from '../utils/sort';
|
import { numberSortComparator } from '../utils/sort';
|
||||||
@ -28,15 +29,13 @@ import {
|
|||||||
getSpanProcessId,
|
getSpanProcessId,
|
||||||
} from './span';
|
} from './span';
|
||||||
|
|
||||||
export const getTraceId = (trace) => trace.traceID;
|
export const getTraceId = (trace: TraceData) => trace.traceID;
|
||||||
|
export const getTraceSpans = (trace: TraceResponse) => trace.spans;
|
||||||
export const getTraceSpans = (trace) => trace.spans;
|
const getTraceProcesses = (trace: TraceData | Trace) => trace.processes;
|
||||||
|
|
||||||
const getTraceProcesses = (trace) => trace.processes;
|
|
||||||
|
|
||||||
const getSpanWithProcess = createSelector(
|
const getSpanWithProcess = createSelector(
|
||||||
(state) => state.span,
|
(state: { span: TraceSpanData; processes: Record<string, TraceProcess> }) => state.span,
|
||||||
(state) => state.processes,
|
(state: { span: TraceSpanData; processes: Record<string, TraceProcess> }) => state.processes,
|
||||||
(span, processes) => ({
|
(span, processes) => ({
|
||||||
...span,
|
...span,
|
||||||
process: processes[getSpanProcessId(span)],
|
process: processes[getSpanProcessId(span)],
|
||||||
@ -44,7 +43,7 @@ const getSpanWithProcess = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getTraceSpansAsMap = createSelector(getTraceSpans, (spans) =>
|
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__';
|
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
|
* @return {TreeNode} A tree of spanIDs derived from the relationships
|
||||||
* between spans in the trace.
|
* between spans in the trace.
|
||||||
*/
|
*/
|
||||||
export function getTraceSpanIdsAsTree(trace) {
|
export function getTraceSpanIdsAsTree(trace: TraceResponse) {
|
||||||
const nodesById = new Map(trace.spans.map((span) => [span.spanID, new TreeNode(span.spanID)]));
|
const nodesById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, new TreeNode(span.spanID)]));
|
||||||
const spansById = new Map(trace.spans.map((span) => [span.spanID, span]));
|
const spansById = new Map(trace.spans.map((span: TraceSpanData) => [span.spanID, span]));
|
||||||
const root = new TreeNode(TREE_ROOT_ID);
|
const root = new TreeNode(TREE_ROOT_ID);
|
||||||
trace.spans.forEach((span) => {
|
trace.spans.forEach((span: TraceSpanData) => {
|
||||||
const node = nodesById.get(span.spanID);
|
const node = nodesById.get(span.spanID)!;
|
||||||
if (Array.isArray(span.references) && span.references.length) {
|
if (Array.isArray(span.references) && span.references.length) {
|
||||||
const { refType, spanID: parentID } = span.references[0];
|
const { refType, spanID: parentID } = span.references[0];
|
||||||
if (refType === 'CHILD_OF' || refType === 'FOLLOWS_FROM') {
|
if (refType === 'CHILD_OF' || refType === 'FOLLOWS_FROM') {
|
||||||
const parent = nodesById.get(parentID) || root;
|
const parent = nodesById.get(parentID) || root;
|
||||||
parent.children.push(node);
|
parent.children?.push(node);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unrecognized ref type: ${refType}`);
|
throw new Error(`Unrecognized ref type: ${refType}`);
|
||||||
}
|
}
|
||||||
@ -81,15 +80,15 @@ export function getTraceSpanIdsAsTree(trace) {
|
|||||||
root.children.push(node);
|
root.children.push(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const comparator = (nodeA, nodeB) => {
|
const comparator = (nodeA: TreeNode | undefined, nodeB: TreeNode | undefined) => {
|
||||||
const a = spansById.get(nodeA.value);
|
const a: TraceSpanData | undefined = nodeA?.value ? spansById.get(nodeA.value.toString()) : undefined;
|
||||||
const b = spansById.get(nodeB.value);
|
const b: TraceSpanData | undefined = nodeB?.value ? spansById.get(nodeB.value.toString()) : undefined;
|
||||||
return +(a.startTime > b.startTime) || +(a.startTime === b.startTime) - 1;
|
return +(a?.startTime! > b?.startTime!) || +(a?.startTime === b?.startTime) - 1;
|
||||||
};
|
};
|
||||||
trace.spans.forEach((span) => {
|
trace.spans.forEach((span: TraceSpanData) => {
|
||||||
const node = nodesById.get(span.spanID);
|
const node: TreeNode | undefined = nodesById.get(span.spanID);
|
||||||
if (node.children.length > 1) {
|
if (node!.children.length > 1) {
|
||||||
node.children.sort(comparator);
|
node?.children.sort(comparator);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
root.children.sort(comparator);
|
root.children.sort(comparator);
|
||||||
@ -97,13 +96,13 @@ export function getTraceSpanIdsAsTree(trace) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// attach "process" as an object to each span.
|
// attach "process" as an object to each span.
|
||||||
export const hydrateSpansWithProcesses = (trace) => {
|
export const hydrateSpansWithProcesses = (trace: TraceResponse) => {
|
||||||
const spans = getTraceSpans(trace);
|
const spans = getTraceSpans(trace);
|
||||||
const processes = getTraceProcesses(trace);
|
const processes = getTraceProcesses(trace);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...trace,
|
...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) =>
|
export const getTraceTimestamp = createSelector(getTraceSpans, (spans) =>
|
||||||
spans.reduce(
|
spans.reduce(
|
||||||
(prevTimestamp, span) => (prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span)),
|
(prevTimestamp: number, span: TraceSpanData) =>
|
||||||
null
|
prevTimestamp ? Math.min(prevTimestamp, getSpanTimestamp(span)) : getSpanTimestamp(span),
|
||||||
|
0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getTraceDuration = createSelector(getTraceSpans, getTraceTimestamp, (spans, timestamp) =>
|
export const getTraceDuration = createSelector(getTraceSpans, getTraceTimestamp, (spans, timestamp) =>
|
||||||
spans.reduce(
|
spans.reduce(
|
||||||
(prevDuration, span) =>
|
(prevDuration: number, span: TraceSpanData) =>
|
||||||
prevDuration
|
prevDuration
|
||||||
? Math.max(getSpanTimestamp(span) - timestamp + getSpanDuration(span), prevDuration)
|
? Math.max(getSpanTimestamp(span) - timestamp! + getSpanDuration(span), prevDuration)
|
||||||
: getSpanDuration(span),
|
: getSpanDuration(span),
|
||||||
null
|
0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getTraceEndTimestamp = createSelector(
|
export const getTraceEndTimestamp = createSelector(
|
||||||
getTraceTimestamp,
|
getTraceTimestamp,
|
||||||
getTraceDuration,
|
getTraceDuration,
|
||||||
(timestamp, duration) => timestamp + duration
|
(timestamp: number, duration: number) => timestamp! + duration
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getParentSpan = createSelector(
|
export const getParentSpan = createSelector(
|
||||||
@ -137,16 +137,18 @@ export const getParentSpan = createSelector(
|
|||||||
getTraceSpansAsMap,
|
getTraceSpansAsMap,
|
||||||
(tree, spanMap) =>
|
(tree, spanMap) =>
|
||||||
tree.children
|
tree.children
|
||||||
.map((node) => spanMap.get(node.value))
|
.map((node: TreeNode) => spanMap.get(node.value))
|
||||||
.sort((spanA, spanB) => numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB)))[0]
|
.sort((spanA: TraceSpanData, spanB: TraceSpanData) =>
|
||||||
|
numberSortComparator(getSpanTimestamp(spanA), getSpanTimestamp(spanB))
|
||||||
|
)[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getTraceDepth = createSelector(getTraceSpanIdsAsTree, (spanTree) => spanTree.depth - 1);
|
export const getTraceDepth = createSelector(getTraceSpanIdsAsTree, (spanTree) => spanTree.depth - 1);
|
||||||
|
|
||||||
export const getSpanDepthForTrace = createSelector(
|
export const getSpanDepthForTrace = createSelector(
|
||||||
createSelector((state) => state.trace, getTraceSpanIdsAsTree),
|
createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree),
|
||||||
createSelector((state) => state.span, getSpanId),
|
createSelector((state: { span: TraceSpanData }) => state.span, getSpanId),
|
||||||
(node, spanID) => node.getPath(spanID).length - 1
|
(node, spanID) => node.getPath(spanID)!.length - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getTraceServices = createSelector(getTraceProcesses, (processes) =>
|
export const getTraceServices = createSelector(getTraceProcesses, (processes) =>
|
||||||
@ -165,26 +167,34 @@ export const DURATION_FORMATTERS = {
|
|||||||
s: formatSecondTime,
|
s: formatSecondTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration) =>
|
const getDurationFormatterForTrace = createSelector(getTraceDuration, (totalDuration: number) =>
|
||||||
totalDuration >= ONE_SECOND ? DURATION_FORMATTERS.s : DURATION_FORMATTERS.ms
|
totalDuration >= ONE_SECOND ? DURATION_FORMATTERS.s : DURATION_FORMATTERS.ms
|
||||||
);
|
);
|
||||||
|
|
||||||
export const formatDurationForUnit = createSelector(
|
export const formatDurationForUnit = createSelector(
|
||||||
({ duration }) => duration,
|
({ duration }: { duration: number }) => duration,
|
||||||
({ unit }) => DURATION_FORMATTERS[unit],
|
({ unit }: { unit: 'ms' | 's' }) => DURATION_FORMATTERS[unit],
|
||||||
(duration, formatter) => formatter(duration)
|
(duration, formatter) => formatter(duration)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const formatDurationForTrace = createSelector(
|
export const formatDurationForTrace = createSelector(
|
||||||
({ duration }) => duration,
|
({ duration }: { duration: number }) => duration,
|
||||||
createSelector(({ trace }) => trace, getDurationFormatterForTrace),
|
createSelector(({ trace }: { trace: TraceResponse }) => trace, getDurationFormatterForTrace),
|
||||||
(duration, formatter) => formatter(duration)
|
(duration, formatter) => formatter(duration)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getSortedSpans = createSelector(
|
export const getSortedSpans = createSelector(
|
||||||
({ trace }) => trace,
|
({ trace }: { trace: TraceResponse }) => trace,
|
||||||
({ spans }) => spans,
|
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||||
({ sort }) => sort,
|
({
|
||||||
|
sort,
|
||||||
|
}: {
|
||||||
|
sort: {
|
||||||
|
dir: number;
|
||||||
|
comparator: (itemA: number, itemB: number) => number;
|
||||||
|
selector: (itemA: TraceSpanData, itemB: TraceResponse) => number;
|
||||||
|
};
|
||||||
|
}) => sort,
|
||||||
(trace, spans, { dir, comparator, selector }) =>
|
(trace, spans, { dir, comparator, selector }) =>
|
||||||
[...spans].sort((spanA, spanB) => dir * comparator(selector(spanA, trace), selector(spanB, trace)))
|
[...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 getTraceSpansByHierarchyPosition = createSelector(getTraceSpanIdsAsTree, (tree) => {
|
||||||
const hierarchyPositionMap = new Map();
|
const hierarchyPositionMap = new Map();
|
||||||
let i = 0;
|
let i = 0;
|
||||||
tree.walk((spanID) => hierarchyPositionMap.set(spanID, i++));
|
tree.walk((spanID: string | number | undefined) => hierarchyPositionMap.set(spanID, i++));
|
||||||
return hierarchyPositionMap;
|
return hierarchyPositionMap;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getTreeSizeForTraceSpan = createSelector(
|
export const getTreeSizeForTraceSpan = createSelector(
|
||||||
createSelector((state) => state.trace, getTraceSpanIdsAsTree),
|
createSelector((state: { trace: TraceResponse }) => state.trace, getTraceSpanIdsAsTree),
|
||||||
createSelector((state) => state.span, getSpanId),
|
createSelector((state: { span: TraceSpanData }) => state.span, getSpanId),
|
||||||
(tree, spanID) => {
|
(tree, spanID) => {
|
||||||
const node = tree.find(spanID);
|
const node = tree.find(spanID);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
@ -209,8 +219,8 @@ export const getTreeSizeForTraceSpan = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getSpanHierarchySortPositionForTrace = createSelector(
|
export const getSpanHierarchySortPositionForTrace = createSelector(
|
||||||
createSelector(({ trace }) => trace, getTraceSpansByHierarchyPosition),
|
createSelector(({ trace }: { trace: Trace }) => trace, getTraceSpansByHierarchyPosition),
|
||||||
({ span }) => span,
|
({ span }: { span: TraceSpan }) => span,
|
||||||
(hierarchyPositionMap, span) => hierarchyPositionMap.get(getSpanId(span))
|
(hierarchyPositionMap, span) => hierarchyPositionMap.get(getSpanId(span))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -222,16 +232,16 @@ export const getTraceName = createSelector(
|
|||||||
serviceName: getSpanServiceName,
|
serviceName: getSpanServiceName,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
({ name, serviceName }) => `${serviceName}: ${name}`
|
({ name, serviceName }: { name: string; serviceName: string }) => `${serviceName}: ${name}`
|
||||||
);
|
);
|
||||||
|
|
||||||
export const omitCollapsedSpans = createSelector(
|
export const omitCollapsedSpans = createSelector(
|
||||||
({ spans }) => spans,
|
({ spans }: { spans: TraceSpanData[] }) => spans,
|
||||||
createSelector(({ trace }) => trace, getTraceSpanIdsAsTree),
|
createSelector(({ trace }: { trace: TraceResponse }) => trace, getTraceSpanIdsAsTree),
|
||||||
({ collapsed }) => collapsed,
|
({ collapsed }: { collapsed: string[] }) => collapsed,
|
||||||
(spans, tree, collapse) => {
|
(spans, tree, collapse) => {
|
||||||
const hiddenSpanIds = collapse.reduce((result, collapsedSpanId) => {
|
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;
|
return result;
|
||||||
}, new Set());
|
}, new Set());
|
||||||
|
|
||||||
@ -242,13 +252,13 @@ export const omitCollapsedSpans = createSelector(
|
|||||||
export const DEFAULT_TICK_INTERVAL = 4;
|
export const DEFAULT_TICK_INTERVAL = 4;
|
||||||
export const DEFAULT_TICK_WIDTH = 3;
|
export const DEFAULT_TICK_WIDTH = 3;
|
||||||
export const getTicksForTrace = createSelector(
|
export const getTicksForTrace = createSelector(
|
||||||
({ trace }) => trace,
|
({ trace }: { trace: TraceResponse }) => trace,
|
||||||
({ interval = DEFAULT_TICK_INTERVAL }) => interval,
|
({ interval = DEFAULT_TICK_INTERVAL }: { interval?: number }) => interval,
|
||||||
({ width = DEFAULT_TICK_WIDTH }) => width,
|
({ width = DEFAULT_TICK_WIDTH }: { width?: number }) => width,
|
||||||
(
|
(
|
||||||
trace,
|
trace,
|
||||||
interval,
|
interval: number,
|
||||||
width
|
width: number
|
||||||
// timestamps will be spaced over the interval, starting from the initial timestamp
|
// timestamps will be spaced over the interval, starting from the initial timestamp
|
||||||
) =>
|
) =>
|
||||||
[...Array(interval + 1).keys()].map((num) => ({
|
[...Array(interval + 1).keys()].map((num) => ({
|
||||||
@ -260,14 +270,16 @@ export const getTicksForTrace = createSelector(
|
|||||||
// TODO: delete this when the backend can ensure uniqueness
|
// TODO: delete this when the backend can ensure uniqueness
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
export const enforceUniqueSpanIds = createSelector(
|
export const enforceUniqueSpanIds = createSelector(
|
||||||
/* istanbul ignore next */ (trace) => trace,
|
/* istanbul ignore next */ (trace: Trace) => trace,
|
||||||
getTraceSpans,
|
getTraceSpans,
|
||||||
/* istanbul ignore next */ (trace, spans) => {
|
/* istanbul ignore next */ (trace, spans) => {
|
||||||
const map = new Map();
|
const map = new Map();
|
||||||
|
|
||||||
|
const spanArray: TraceSpanData[] = [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...trace,
|
...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 spanID = map.has(getSpanId(span)) ? `${getSpanId(span)}_${map.get(getSpanId(span))}` : getSpanId(span);
|
||||||
const updatedSpan = { ...span, spanID };
|
const updatedSpan = { ...span, spanID };
|
||||||
|
|
||||||
@ -280,17 +292,17 @@ export const enforceUniqueSpanIds = createSelector(
|
|||||||
map.set(getSpanId(span), (map.get(getSpanId(span)) || 0) + 1);
|
map.set(getSpanId(span), (map.get(getSpanId(span)) || 0) + 1);
|
||||||
|
|
||||||
return result.concat([updatedSpan]);
|
return result.concat([updatedSpan]);
|
||||||
}, []),
|
}, spanArray),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: delete this when the backend can ensure uniqueness
|
// TODO: delete this when the backend can ensure uniqueness
|
||||||
export const dropEmptyStartTimeSpans = createSelector(
|
export const dropEmptyStartTimeSpans = createSelector(
|
||||||
/* istanbul ignore next */ (trace) => trace,
|
/* istanbul ignore next */ (trace: Trace) => trace,
|
||||||
getTraceSpans,
|
getTraceSpans,
|
||||||
/* istanbul ignore next */ (trace, spans) => ({
|
/* istanbul ignore next */ (trace, spans) => ({
|
||||||
...trace,
|
...trace,
|
||||||
spans: spans.filter((span) => !!getSpanTimestamp(span)),
|
spans: spans.filter((span: TraceSpanData) => !!getSpanTimestamp(span)),
|
||||||
})
|
})
|
||||||
);
|
);
|
@ -22,7 +22,7 @@ export default class TreeNode {
|
|||||||
return (node: TreeNode) => fn(node.value, node, depth);
|
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') {
|
if (typeof search === 'function') {
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ export default class TreeNode {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
find(search: TreeNode | number | SearchFn): TreeNode | null {
|
find(search: TreeNode | number | SearchFn | string): TreeNode | null {
|
||||||
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
||||||
if (searchFn(this)) {
|
if (searchFn(this)) {
|
||||||
return this;
|
return this;
|
||||||
@ -65,7 +65,7 @@ export default class TreeNode {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPath(search: TreeNode) {
|
getPath(search: TreeNode | string) {
|
||||||
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
const searchFn = TreeNode.iterFunction(TreeNode.searchFunction(search));
|
||||||
|
|
||||||
const findPath = (currentNode: TreeNode, currentPath: TreeNode[]): TreeNode[] | null => {
|
const findPath = (currentNode: TreeNode, currentPath: TreeNode[]): TreeNode[] | null => {
|
||||||
|
Loading…
Reference in New Issue
Block a user