@perses-dev/tempo-plugin 0.46.0 → 0.47.0-rc0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cjs/model/api-types.js +23 -1
  2. package/dist/cjs/model/tempo-client.js +51 -28
  3. package/dist/cjs/plugins/tempo-datasource.js +3 -3
  4. package/dist/cjs/plugins/tempo-trace-query/TempoTraceQuery.js +1 -1
  5. package/dist/cjs/plugins/tempo-trace-query/get-trace-data.js +128 -32
  6. package/dist/cjs/test/mock-data.js +2461 -796
  7. package/dist/model/api-types.d.ts +88 -51
  8. package/dist/model/api-types.d.ts.map +1 -1
  9. package/dist/model/api-types.js +4 -4
  10. package/dist/model/api-types.js.map +1 -1
  11. package/dist/model/tempo-client.d.ts +18 -16
  12. package/dist/model/tempo-client.d.ts.map +1 -1
  13. package/dist/model/tempo-client.js +54 -33
  14. package/dist/model/tempo-client.js.map +1 -1
  15. package/dist/plugins/tempo-datasource.d.ts.map +1 -1
  16. package/dist/plugins/tempo-datasource.js +4 -4
  17. package/dist/plugins/tempo-datasource.js.map +1 -1
  18. package/dist/plugins/tempo-trace-query/TempoTraceQuery.js +1 -1
  19. package/dist/plugins/tempo-trace-query/TempoTraceQuery.js.map +1 -1
  20. package/dist/plugins/tempo-trace-query/get-trace-data.d.ts.map +1 -1
  21. package/dist/plugins/tempo-trace-query/get-trace-data.js +128 -32
  22. package/dist/plugins/tempo-trace-query/get-trace-data.js.map +1 -1
  23. package/dist/test/mock-data.d.ts +7 -3
  24. package/dist/test/mock-data.d.ts.map +1 -1
  25. package/dist/test/mock-data.js +2444 -791
  26. package/dist/test/mock-data.js.map +1 -1
  27. package/package.json +4 -4
  28. package/dist/cjs/plugins/TempoTraceQuery.js +0 -30
  29. package/dist/cjs/plugins/get-trace-data.js +0 -69
  30. package/dist/plugins/TempoTraceQuery.d.ts +0 -11
  31. package/dist/plugins/TempoTraceQuery.d.ts.map +0 -1
  32. package/dist/plugins/TempoTraceQuery.js +0 -24
  33. package/dist/plugins/TempoTraceQuery.js.map +0 -1
  34. package/dist/plugins/get-trace-data.d.ts +0 -4
  35. package/dist/plugins/get-trace-data.d.ts.map +0 -1
  36. package/dist/plugins/get-trace-data.js +0 -61
  37. package/dist/plugins/get-trace-data.js.map +0 -1
@@ -10,7 +10,9 @@
10
10
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
+ import { isValidTraceId } from '@perses-dev/core';
13
14
  import { getUnixTime } from 'date-fns';
15
+ import { sortedIndexBy } from 'lodash';
14
16
  import { TEMPO_DATASOURCE_KIND } from '../../model/tempo-selectors';
15
17
  export function getUnixTimeRange(timeRange) {
16
18
  const { start, end } = timeRange;
@@ -25,7 +27,7 @@ export const getTraceData = async (spec, context)=>{
25
27
  // Do not make a request to the backend, instead return an empty TraceData
26
28
  console.error('TempoTraceQuery is undefined, null, or an empty string.');
27
29
  return {
28
- traces: []
30
+ searchResult: []
29
31
  };
30
32
  }
31
33
  const defaultTempoDatasource = {
@@ -37,49 +39,143 @@ export const getTraceData = async (spec, context)=>{
37
39
  if (datasourceUrl === undefined || datasourceUrl === null || datasourceUrl === '') {
38
40
  console.error('TempoDatasource is undefined, null, or an empty string.');
39
41
  return {
40
- traces: []
42
+ searchResult: []
41
43
  };
42
44
  }
43
45
  const getQuery = ()=>{
44
46
  // if time range not defined -- only return the query from the spec
45
47
  if (context.absoluteTimeRange === undefined) {
46
- return spec.query;
47
- }
48
- // if the query already contains a time range (i.e.start and end times)
49
- if (spec.query.includes('start=') || spec.query.includes('end=')) {
50
- return spec.query;
48
+ return {
49
+ q: spec.query
50
+ };
51
51
  }
52
52
  // handle time range selection from UI drop down (e.g. last 5 minutes, last 1 hour )
53
53
  const { start, end } = getUnixTimeRange(context === null || context === void 0 ? void 0 : context.absoluteTimeRange);
54
- const queryStartTime = '&start=' + start;
55
- const queryEndTime = '&end=' + end;
56
- const queryWithTimeRange = encodeURI(spec.query) + queryStartTime + queryEndTime;
57
- return queryWithTimeRange;
54
+ return {
55
+ q: spec.query,
56
+ start,
57
+ end
58
+ };
58
59
  };
59
- const enrichedTraceResponse = await client.getEnrichedTraceQuery(getQuery(), datasourceUrl);
60
- const traces = enrichedTraceResponse.traces.map((traceValue)=>{
61
- const startTimeUnixMs = parseInt(traceValue.summary.startTimeUnixNano) * 1e-6; // convert to millisecond for eChart time format
62
- const durationMs = traceValue.summary.durationMs;
63
- const spanCount = traceValue.spanCount;
64
- const errorCount = traceValue.errorCount;
65
- const traceId = traceValue.summary.traceID;
66
- const name = `rootServiceName="${traceValue.summary.rootServiceName.trim()}", rootTraceName="${traceValue.summary.rootServiceName.trim()}"`;
60
+ /**
61
+ * determine type of query:
62
+ * if the query is a valid traceId, fetch the trace by traceId
63
+ * otherwise, execute a TraceQL query
64
+ */ if (isValidTraceId(spec.query)) {
65
+ const response = await client.searchTraceID(spec.query, {
66
+ datasourceUrl
67
+ });
67
68
  return {
68
- startTimeUnixMs,
69
- durationMs,
70
- spanCount,
71
- errorCount,
72
- traceId,
73
- name
69
+ trace: parseTraceResponse(response),
70
+ metadata: {
71
+ executedQueryString: spec.query
72
+ }
74
73
  };
75
- });
76
- const traceData = {
77
- traces,
78
- metadata: {
79
- executedQueryString: spec.query
74
+ } else {
75
+ const response = await client.searchTraceQueryFallback(getQuery(), {
76
+ datasourceUrl
77
+ });
78
+ return {
79
+ searchResult: parseSearchResponse(response),
80
+ metadata: {
81
+ executedQueryString: spec.query
82
+ }
83
+ };
84
+ }
85
+ };
86
+ function parseResource(resource) {
87
+ let serviceName = 'unknown';
88
+ for (const attr of resource.attributes){
89
+ if (attr.key === 'service.name' && 'stringValue' in attr.value) {
90
+ serviceName = attr.value.stringValue;
91
+ break;
80
92
  }
93
+ }
94
+ return {
95
+ serviceName,
96
+ attributes: resource.attributes
81
97
  };
82
- return traceData;
83
- };
98
+ }
99
+ function parseEvent(event) {
100
+ return {
101
+ timeUnixMs: parseInt(event.timeUnixNano) * 1e-6,
102
+ name: event.name,
103
+ attributes: event.attributes || []
104
+ };
105
+ }
106
+ /**
107
+ * parseSpan parses the Span API type to the internal representation
108
+ * i.e. convert strings to numbers etc.
109
+ */ function parseSpan(span) {
110
+ return {
111
+ traceId: span.traceId,
112
+ spanId: span.spanId,
113
+ parentSpanId: span.parentSpanId,
114
+ name: span.name,
115
+ kind: span.kind,
116
+ startTimeUnixMs: parseInt(span.startTimeUnixNano) * 1e-6,
117
+ endTimeUnixMs: parseInt(span.endTimeUnixNano) * 1e-6,
118
+ attributes: span.attributes || [],
119
+ events: (span.events || []).map(parseEvent),
120
+ status: span.status
121
+ };
122
+ }
123
+ /**
124
+ * parseTraceResponse builds a tree of spans from the Tempo API response
125
+ * time complexity: O(2n)
126
+ */ function parseTraceResponse(response) {
127
+ // first pass: build lookup table <spanId, Span>
128
+ const lookup = new Map();
129
+ for (const batch of response.batches){
130
+ const resource = parseResource(batch.resource);
131
+ for (const scopeSpan of batch.scopeSpans){
132
+ const scope = scopeSpan.scope;
133
+ for (const tempoSpan of scopeSpan.spans){
134
+ const span = {
135
+ resource,
136
+ scope,
137
+ childSpans: [],
138
+ ...parseSpan(tempoSpan)
139
+ };
140
+ lookup.set(tempoSpan.spanId, span);
141
+ }
142
+ }
143
+ }
144
+ // second pass: build tree based on parentSpanId property
145
+ let rootSpan = null;
146
+ for (const [, span] of lookup){
147
+ if (!span.parentSpanId) {
148
+ rootSpan = span;
149
+ } else {
150
+ const parent = lookup.get(span.parentSpanId);
151
+ if (!parent) {
152
+ console.error(`span ${span.spanId} has parent ${span.parentSpanId} which has not been received yet`);
153
+ continue;
154
+ }
155
+ span.parentSpan = parent;
156
+ const insertChildSpanAt = sortedIndexBy(parent.childSpans, span, (s)=>s.startTimeUnixMs);
157
+ parent.childSpans.splice(insertChildSpanAt, 0, span);
158
+ }
159
+ }
160
+ if (!rootSpan) {
161
+ throw new Error('root span not found');
162
+ }
163
+ return {
164
+ rootSpan
165
+ };
166
+ }
167
+ function parseSearchResponse(response) {
168
+ return response.traces.map((trace)=>{
169
+ var _trace_durationMs;
170
+ return {
171
+ startTimeUnixMs: parseInt(trace.startTimeUnixNano) * 1e-6,
172
+ durationMs: (_trace_durationMs = trace.durationMs) !== null && _trace_durationMs !== void 0 ? _trace_durationMs : 0,
173
+ traceId: trace.traceID,
174
+ rootServiceName: trace.rootServiceName,
175
+ rootTraceName: trace.rootTraceName,
176
+ serviceStats: trace.serviceStats || {}
177
+ };
178
+ });
179
+ }
84
180
 
85
181
  //# sourceMappingURL=get-trace-data.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/plugins/tempo-trace-query/get-trace-data.ts"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { TraceQueryPlugin } from '@perses-dev/plugin-system';\nimport { TraceData, TraceValue, AbsoluteTimeRange } from '@perses-dev/core';\nimport { getUnixTime } from 'date-fns';\nimport { TempoTraceQuerySpec } from '../../model/trace-query-model';\nimport { TEMPO_DATASOURCE_KIND, TempoDatasourceSelector } from '../../model/tempo-selectors';\nimport { TempoClient } from '../../model/tempo-client';\n\nexport function getUnixTimeRange(timeRange: AbsoluteTimeRange) {\n const { start, end } = timeRange;\n return {\n start: Math.ceil(getUnixTime(start)),\n end: Math.ceil(getUnixTime(end)),\n };\n}\n\nexport const getTraceData: TraceQueryPlugin<TempoTraceQuerySpec>['getTraceData'] = async (spec, context) => {\n if (spec.query === undefined || spec.query === null || spec.query === '') {\n // Do not make a request to the backend, instead return an empty TraceData\n console.error('TempoTraceQuery is undefined, null, or an empty string.');\n return { traces: [] };\n }\n\n const defaultTempoDatasource: TempoDatasourceSelector = {\n kind: TEMPO_DATASOURCE_KIND,\n };\n\n const client: TempoClient = await context.datasourceStore.getDatasourceClient(\n spec.datasource ?? defaultTempoDatasource\n );\n\n const datasourceUrl = client?.options?.datasourceUrl;\n if (datasourceUrl === undefined || datasourceUrl === null || datasourceUrl === '') {\n console.error('TempoDatasource is undefined, null, or an empty string.');\n return { traces: [] };\n }\n\n const getQuery = () => {\n // if time range not defined -- only return the query from the spec\n if (context.absoluteTimeRange === undefined) {\n return spec.query;\n }\n // if the query already contains a time range (i.e.start and end times)\n if (spec.query.includes('start=') || spec.query.includes('end=')) {\n return spec.query;\n }\n // handle time range selection from UI drop down (e.g. last 5 minutes, last 1 hour )\n const { start, end } = getUnixTimeRange(context?.absoluteTimeRange);\n const queryStartTime = '&start=' + start;\n const queryEndTime = '&end=' + end;\n const queryWithTimeRange = encodeURI(spec.query) + queryStartTime + queryEndTime;\n return queryWithTimeRange;\n };\n\n const enrichedTraceResponse = await client.getEnrichedTraceQuery(getQuery(), datasourceUrl);\n\n const traces: TraceValue[] = enrichedTraceResponse.traces.map((traceValue) => {\n const startTimeUnixMs = parseInt(traceValue.summary.startTimeUnixNano) * 1e-6; // convert to millisecond for eChart time format\n const durationMs = traceValue.summary.durationMs;\n const spanCount = traceValue.spanCount;\n const errorCount = traceValue.errorCount;\n const traceId = traceValue.summary.traceID;\n const name = `rootServiceName=\"${traceValue.summary.rootServiceName.trim()}\", rootTraceName=\"${traceValue.summary.rootServiceName.trim()}\"`;\n\n return {\n startTimeUnixMs,\n durationMs,\n spanCount,\n errorCount,\n traceId,\n name,\n };\n });\n\n const traceData: TraceData = {\n traces,\n metadata: {\n executedQueryString: spec.query,\n },\n };\n\n return traceData;\n};\n"],"names":["getUnixTime","TEMPO_DATASOURCE_KIND","getUnixTimeRange","timeRange","start","end","Math","ceil","getTraceData","spec","context","client","query","undefined","console","error","traces","defaultTempoDatasource","kind","datasourceStore","getDatasourceClient","datasource","datasourceUrl","options","getQuery","absoluteTimeRange","includes","queryStartTime","queryEndTime","queryWithTimeRange","encodeURI","enrichedTraceResponse","getEnrichedTraceQuery","map","traceValue","startTimeUnixMs","parseInt","summary","startTimeUnixNano","durationMs","spanCount","errorCount","traceId","traceID","name","rootServiceName","trim","traceData","metadata","executedQueryString"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAIjC,SAASA,WAAW,QAAQ,WAAW;AAEvC,SAASC,qBAAqB,QAAiC,8BAA8B;AAG7F,OAAO,SAASC,iBAAiBC,SAA4B;IAC3D,MAAM,EAAEC,KAAK,EAAEC,GAAG,EAAE,GAAGF;IACvB,OAAO;QACLC,OAAOE,KAAKC,IAAI,CAACP,YAAYI;QAC7BC,KAAKC,KAAKC,IAAI,CAACP,YAAYK;IAC7B;AACF;AAEA,OAAO,MAAMG,eAAsE,OAAOC,MAAMC;QAexEC;IAdtB,IAAIF,KAAKG,KAAK,KAAKC,aAAaJ,KAAKG,KAAK,KAAK,QAAQH,KAAKG,KAAK,KAAK,IAAI;QACxE,0EAA0E;QAC1EE,QAAQC,KAAK,CAAC;QACd,OAAO;YAAEC,QAAQ,EAAE;QAAC;IACtB;IAEA,MAAMC,yBAAkD;QACtDC,MAAMjB;IACR;QAGEQ;IADF,MAAME,SAAsB,MAAMD,QAAQS,eAAe,CAACC,mBAAmB,CAC3EX,CAAAA,mBAAAA,KAAKY,UAAU,cAAfZ,8BAAAA,mBAAmBQ;IAGrB,MAAMK,gBAAgBX,mBAAAA,8BAAAA,kBAAAA,OAAQY,OAAO,cAAfZ,sCAAAA,gBAAiBW,aAAa;IACpD,IAAIA,kBAAkBT,aAAaS,kBAAkB,QAAQA,kBAAkB,IAAI;QACjFR,QAAQC,KAAK,CAAC;QACd,OAAO;YAAEC,QAAQ,EAAE;QAAC;IACtB;IAEA,MAAMQ,WAAW;QACf,mEAAmE;QACnE,IAAId,QAAQe,iBAAiB,KAAKZ,WAAW;YAC3C,OAAOJ,KAAKG,KAAK;QACnB;QACA,uEAAuE;QACvE,IAAIH,KAAKG,KAAK,CAACc,QAAQ,CAAC,aAAajB,KAAKG,KAAK,CAACc,QAAQ,CAAC,SAAS;YAChE,OAAOjB,KAAKG,KAAK;QACnB;QACA,oFAAoF;QACpF,MAAM,EAAER,KAAK,EAAEC,GAAG,EAAE,GAAGH,iBAAiBQ,oBAAAA,8BAAAA,QAASe,iBAAiB;QAClE,MAAME,iBAAiB,YAAYvB;QACnC,MAAMwB,eAAe,UAAUvB;QAC/B,MAAMwB,qBAAqBC,UAAUrB,KAAKG,KAAK,IAAIe,iBAAiBC;QACpE,OAAOC;IACT;IAEA,MAAME,wBAAwB,MAAMpB,OAAOqB,qBAAqB,CAACR,YAAYF;IAE7E,MAAMN,SAAuBe,sBAAsBf,MAAM,CAACiB,GAAG,CAAC,CAACC;QAC7D,MAAMC,kBAAkBC,SAASF,WAAWG,OAAO,CAACC,iBAAiB,IAAI,MAAM,gDAAgD;QAC/H,MAAMC,aAAaL,WAAWG,OAAO,CAACE,UAAU;QAChD,MAAMC,YAAYN,WAAWM,SAAS;QACtC,MAAMC,aAAaP,WAAWO,UAAU;QACxC,MAAMC,UAAUR,WAAWG,OAAO,CAACM,OAAO;QAC1C,MAAMC,OAAO,CAAC,iBAAiB,EAAEV,WAAWG,OAAO,CAACQ,eAAe,CAACC,IAAI,GAAG,kBAAkB,EAAEZ,WAAWG,OAAO,CAACQ,eAAe,CAACC,IAAI,GAAG,CAAC,CAAC;QAE3I,OAAO;YACLX;YACAI;YACAC;YACAC;YACAC;YACAE;QACF;IACF;IAEA,MAAMG,YAAuB;QAC3B/B;QACAgC,UAAU;YACRC,qBAAqBxC,KAAKG,KAAK;QACjC;IACF;IAEA,OAAOmC;AACT,EAAE"}
1
+ {"version":3,"sources":["../../../src/plugins/tempo-trace-query/get-trace-data.ts"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { TraceQueryPlugin } from '@perses-dev/plugin-system';\nimport {\n TraceSearchResult,\n AbsoluteTimeRange,\n Trace,\n Span,\n isValidTraceId,\n TraceResource,\n SpanEvent,\n} from '@perses-dev/core';\nimport { getUnixTime } from 'date-fns';\nimport { sortedIndexBy } from 'lodash';\nimport { TempoTraceQuerySpec } from '../../model/trace-query-model';\nimport { TEMPO_DATASOURCE_KIND, TempoDatasourceSelector } from '../../model/tempo-selectors';\nimport { TempoClient } from '../../model/tempo-client';\nimport {\n SearchRequestParameters,\n SearchTraceIDResponse,\n SearchTraceQueryResponse,\n Resource as TempoResource,\n Span as TempoSpan,\n SpanEvent as TempoSpanEvent,\n} from '../../model/api-types';\n\nexport function getUnixTimeRange(timeRange: AbsoluteTimeRange) {\n const { start, end } = timeRange;\n return {\n start: Math.ceil(getUnixTime(start)),\n end: Math.ceil(getUnixTime(end)),\n };\n}\n\nexport const getTraceData: TraceQueryPlugin<TempoTraceQuerySpec>['getTraceData'] = async (spec, context) => {\n if (spec.query === undefined || spec.query === null || spec.query === '') {\n // Do not make a request to the backend, instead return an empty TraceData\n console.error('TempoTraceQuery is undefined, null, or an empty string.');\n return { searchResult: [] };\n }\n\n const defaultTempoDatasource: TempoDatasourceSelector = {\n kind: TEMPO_DATASOURCE_KIND,\n };\n\n const client: TempoClient = await context.datasourceStore.getDatasourceClient(\n spec.datasource ?? defaultTempoDatasource\n );\n\n const datasourceUrl = client?.options?.datasourceUrl;\n if (datasourceUrl === undefined || datasourceUrl === null || datasourceUrl === '') {\n console.error('TempoDatasource is undefined, null, or an empty string.');\n return { searchResult: [] };\n }\n\n const getQuery = (): SearchRequestParameters => {\n // if time range not defined -- only return the query from the spec\n if (context.absoluteTimeRange === undefined) {\n return { q: spec.query };\n }\n // handle time range selection from UI drop down (e.g. last 5 minutes, last 1 hour )\n const { start, end } = getUnixTimeRange(context?.absoluteTimeRange);\n return {\n q: spec.query,\n start,\n end,\n };\n };\n\n /**\n * determine type of query:\n * if the query is a valid traceId, fetch the trace by traceId\n * otherwise, execute a TraceQL query\n */\n if (isValidTraceId(spec.query)) {\n const response = await client.searchTraceID(spec.query, { datasourceUrl });\n return {\n trace: parseTraceResponse(response),\n metadata: {\n executedQueryString: spec.query,\n },\n };\n } else {\n const response = await client.searchTraceQueryFallback(getQuery(), { datasourceUrl });\n return {\n searchResult: parseSearchResponse(response),\n metadata: {\n executedQueryString: spec.query,\n },\n };\n }\n};\n\nfunction parseResource(resource: TempoResource): TraceResource {\n let serviceName = 'unknown';\n for (const attr of resource.attributes) {\n if (attr.key === 'service.name' && 'stringValue' in attr.value) {\n serviceName = attr.value.stringValue;\n break;\n }\n }\n\n return {\n serviceName,\n attributes: resource.attributes,\n };\n}\n\nfunction parseEvent(event: TempoSpanEvent): SpanEvent {\n return {\n timeUnixMs: parseInt(event.timeUnixNano) * 1e-6, // convert to milliseconds because JS cannot handle numbers larger than 9007199254740991\n name: event.name,\n attributes: event.attributes || [],\n };\n}\n\n/**\n * parseSpan parses the Span API type to the internal representation\n * i.e. convert strings to numbers etc.\n */\nfunction parseSpan(span: TempoSpan) {\n return {\n traceId: span.traceId,\n spanId: span.spanId,\n parentSpanId: span.parentSpanId,\n name: span.name,\n kind: span.kind,\n startTimeUnixMs: parseInt(span.startTimeUnixNano) * 1e-6, // convert to milliseconds because JS cannot handle numbers larger than 9007199254740991\n endTimeUnixMs: parseInt(span.endTimeUnixNano) * 1e-6,\n attributes: span.attributes || [],\n events: (span.events || []).map(parseEvent),\n status: span.status,\n };\n}\n\n/**\n * parseTraceResponse builds a tree of spans from the Tempo API response\n * time complexity: O(2n)\n */\nfunction parseTraceResponse(response: SearchTraceIDResponse): Trace {\n // first pass: build lookup table <spanId, Span>\n const lookup = new Map<string, Span>();\n for (const batch of response.batches) {\n const resource = parseResource(batch.resource);\n\n for (const scopeSpan of batch.scopeSpans) {\n const scope = scopeSpan.scope;\n\n for (const tempoSpan of scopeSpan.spans) {\n const span: Span = {\n resource,\n scope,\n childSpans: [],\n ...parseSpan(tempoSpan),\n };\n lookup.set(tempoSpan.spanId, span);\n }\n }\n }\n\n // second pass: build tree based on parentSpanId property\n let rootSpan: Span | null = null;\n for (const [, span] of lookup) {\n if (!span.parentSpanId) {\n rootSpan = span;\n } else {\n const parent = lookup.get(span.parentSpanId);\n if (!parent) {\n console.error(`span ${span.spanId} has parent ${span.parentSpanId} which has not been received yet`);\n continue;\n }\n\n span.parentSpan = parent;\n const insertChildSpanAt = sortedIndexBy(parent.childSpans, span, (s) => s.startTimeUnixMs);\n parent.childSpans.splice(insertChildSpanAt, 0, span);\n }\n }\n\n if (!rootSpan) {\n throw new Error('root span not found');\n }\n\n return {\n rootSpan,\n };\n}\n\nfunction parseSearchResponse(response: SearchTraceQueryResponse): TraceSearchResult[] {\n return response.traces.map((trace) => ({\n startTimeUnixMs: parseInt(trace.startTimeUnixNano) * 1e-6, // convert to millisecond for eChart time format,\n durationMs: trace.durationMs ?? 0, // Tempo API doesn't return 0 values\n traceId: trace.traceID,\n rootServiceName: trace.rootServiceName,\n rootTraceName: trace.rootTraceName,\n serviceStats: trace.serviceStats || {},\n }));\n}\n"],"names":["isValidTraceId","getUnixTime","sortedIndexBy","TEMPO_DATASOURCE_KIND","getUnixTimeRange","timeRange","start","end","Math","ceil","getTraceData","spec","context","client","query","undefined","console","error","searchResult","defaultTempoDatasource","kind","datasourceStore","getDatasourceClient","datasource","datasourceUrl","options","getQuery","absoluteTimeRange","q","response","searchTraceID","trace","parseTraceResponse","metadata","executedQueryString","searchTraceQueryFallback","parseSearchResponse","parseResource","resource","serviceName","attr","attributes","key","value","stringValue","parseEvent","event","timeUnixMs","parseInt","timeUnixNano","name","parseSpan","span","traceId","spanId","parentSpanId","startTimeUnixMs","startTimeUnixNano","endTimeUnixMs","endTimeUnixNano","events","map","status","lookup","Map","batch","batches","scopeSpan","scopeSpans","scope","tempoSpan","spans","childSpans","set","rootSpan","parent","get","parentSpan","insertChildSpanAt","s","splice","Error","traces","durationMs","traceID","rootServiceName","rootTraceName","serviceStats"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAGjC,SAKEA,cAAc,QAGT,mBAAmB;AAC1B,SAASC,WAAW,QAAQ,WAAW;AACvC,SAASC,aAAa,QAAQ,SAAS;AAEvC,SAASC,qBAAqB,QAAiC,8BAA8B;AAW7F,OAAO,SAASC,iBAAiBC,SAA4B;IAC3D,MAAM,EAAEC,KAAK,EAAEC,GAAG,EAAE,GAAGF;IACvB,OAAO;QACLC,OAAOE,KAAKC,IAAI,CAACR,YAAYK;QAC7BC,KAAKC,KAAKC,IAAI,CAACR,YAAYM;IAC7B;AACF;AAEA,OAAO,MAAMG,eAAsE,OAAOC,MAAMC;QAexEC;IAdtB,IAAIF,KAAKG,KAAK,KAAKC,aAAaJ,KAAKG,KAAK,KAAK,QAAQH,KAAKG,KAAK,KAAK,IAAI;QACxE,0EAA0E;QAC1EE,QAAQC,KAAK,CAAC;QACd,OAAO;YAAEC,cAAc,EAAE;QAAC;IAC5B;IAEA,MAAMC,yBAAkD;QACtDC,MAAMjB;IACR;QAGEQ;IADF,MAAME,SAAsB,MAAMD,QAAQS,eAAe,CAACC,mBAAmB,CAC3EX,CAAAA,mBAAAA,KAAKY,UAAU,cAAfZ,8BAAAA,mBAAmBQ;IAGrB,MAAMK,gBAAgBX,mBAAAA,8BAAAA,kBAAAA,OAAQY,OAAO,cAAfZ,sCAAAA,gBAAiBW,aAAa;IACpD,IAAIA,kBAAkBT,aAAaS,kBAAkB,QAAQA,kBAAkB,IAAI;QACjFR,QAAQC,KAAK,CAAC;QACd,OAAO;YAAEC,cAAc,EAAE;QAAC;IAC5B;IAEA,MAAMQ,WAAW;QACf,mEAAmE;QACnE,IAAId,QAAQe,iBAAiB,KAAKZ,WAAW;YAC3C,OAAO;gBAAEa,GAAGjB,KAAKG,KAAK;YAAC;QACzB;QACA,oFAAoF;QACpF,MAAM,EAAER,KAAK,EAAEC,GAAG,EAAE,GAAGH,iBAAiBQ,oBAAAA,8BAAAA,QAASe,iBAAiB;QAClE,OAAO;YACLC,GAAGjB,KAAKG,KAAK;YACbR;YACAC;QACF;IACF;IAEA;;;;GAIC,GACD,IAAIP,eAAeW,KAAKG,KAAK,GAAG;QAC9B,MAAMe,WAAW,MAAMhB,OAAOiB,aAAa,CAACnB,KAAKG,KAAK,EAAE;YAAEU;QAAc;QACxE,OAAO;YACLO,OAAOC,mBAAmBH;YAC1BI,UAAU;gBACRC,qBAAqBvB,KAAKG,KAAK;YACjC;QACF;IACF,OAAO;QACL,MAAMe,WAAW,MAAMhB,OAAOsB,wBAAwB,CAACT,YAAY;YAAEF;QAAc;QACnF,OAAO;YACLN,cAAckB,oBAAoBP;YAClCI,UAAU;gBACRC,qBAAqBvB,KAAKG,KAAK;YACjC;QACF;IACF;AACF,EAAE;AAEF,SAASuB,cAAcC,QAAuB;IAC5C,IAAIC,cAAc;IAClB,KAAK,MAAMC,QAAQF,SAASG,UAAU,CAAE;QACtC,IAAID,KAAKE,GAAG,KAAK,kBAAkB,iBAAiBF,KAAKG,KAAK,EAAE;YAC9DJ,cAAcC,KAAKG,KAAK,CAACC,WAAW;YACpC;QACF;IACF;IAEA,OAAO;QACLL;QACAE,YAAYH,SAASG,UAAU;IACjC;AACF;AAEA,SAASI,WAAWC,KAAqB;IACvC,OAAO;QACLC,YAAYC,SAASF,MAAMG,YAAY,IAAI;QAC3CC,MAAMJ,MAAMI,IAAI;QAChBT,YAAYK,MAAML,UAAU,IAAI,EAAE;IACpC;AACF;AAEA;;;CAGC,GACD,SAASU,UAAUC,IAAe;IAChC,OAAO;QACLC,SAASD,KAAKC,OAAO;QACrBC,QAAQF,KAAKE,MAAM;QACnBC,cAAcH,KAAKG,YAAY;QAC/BL,MAAME,KAAKF,IAAI;QACf9B,MAAMgC,KAAKhC,IAAI;QACfoC,iBAAiBR,SAASI,KAAKK,iBAAiB,IAAI;QACpDC,eAAeV,SAASI,KAAKO,eAAe,IAAI;QAChDlB,YAAYW,KAAKX,UAAU,IAAI,EAAE;QACjCmB,QAAQ,AAACR,CAAAA,KAAKQ,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAChB;QAChCiB,QAAQV,KAAKU,MAAM;IACrB;AACF;AAEA;;;CAGC,GACD,SAAS9B,mBAAmBH,QAA+B;IACzD,gDAAgD;IAChD,MAAMkC,SAAS,IAAIC;IACnB,KAAK,MAAMC,SAASpC,SAASqC,OAAO,CAAE;QACpC,MAAM5B,WAAWD,cAAc4B,MAAM3B,QAAQ;QAE7C,KAAK,MAAM6B,aAAaF,MAAMG,UAAU,CAAE;YACxC,MAAMC,QAAQF,UAAUE,KAAK;YAE7B,KAAK,MAAMC,aAAaH,UAAUI,KAAK,CAAE;gBACvC,MAAMnB,OAAa;oBACjBd;oBACA+B;oBACAG,YAAY,EAAE;oBACd,GAAGrB,UAAUmB,UAAU;gBACzB;gBACAP,OAAOU,GAAG,CAACH,UAAUhB,MAAM,EAAEF;YAC/B;QACF;IACF;IAEA,yDAAyD;IACzD,IAAIsB,WAAwB;IAC5B,KAAK,MAAM,GAAGtB,KAAK,IAAIW,OAAQ;QAC7B,IAAI,CAACX,KAAKG,YAAY,EAAE;YACtBmB,WAAWtB;QACb,OAAO;YACL,MAAMuB,SAASZ,OAAOa,GAAG,CAACxB,KAAKG,YAAY;YAC3C,IAAI,CAACoB,QAAQ;gBACX3D,QAAQC,KAAK,CAAC,CAAC,KAAK,EAAEmC,KAAKE,MAAM,CAAC,YAAY,EAAEF,KAAKG,YAAY,CAAC,gCAAgC,CAAC;gBACnG;YACF;YAEAH,KAAKyB,UAAU,GAAGF;YAClB,MAAMG,oBAAoB5E,cAAcyE,OAAOH,UAAU,EAAEpB,MAAM,CAAC2B,IAAMA,EAAEvB,eAAe;YACzFmB,OAAOH,UAAU,CAACQ,MAAM,CAACF,mBAAmB,GAAG1B;QACjD;IACF;IAEA,IAAI,CAACsB,UAAU;QACb,MAAM,IAAIO,MAAM;IAClB;IAEA,OAAO;QACLP;IACF;AACF;AAEA,SAAStC,oBAAoBP,QAAkC;IAC7D,OAAOA,SAASqD,MAAM,CAACrB,GAAG,CAAC,CAAC9B;YAEdA;eAFyB;YACrCyB,iBAAiBR,SAASjB,MAAM0B,iBAAiB,IAAI;YACrD0B,YAAYpD,CAAAA,oBAAAA,MAAMoD,UAAU,cAAhBpD,+BAAAA,oBAAoB;YAChCsB,SAAStB,MAAMqD,OAAO;YACtBC,iBAAiBtD,MAAMsD,eAAe;YACtCC,eAAevD,MAAMuD,aAAa;YAClCC,cAAcxD,MAAMwD,YAAY,IAAI,CAAC;QACvC;IAAA;AACF"}
@@ -1,5 +1,9 @@
1
1
  import { TraceData } from '@perses-dev/core';
2
- import { EnrichedTraceQueryResponse } from '../model/api-types';
3
- export declare const MOCK_ENRICHED_TRACE_QUERY_RESPONSE: EnrichedTraceQueryResponse;
4
- export declare const MOCK_TRACE_DATA: TraceData;
2
+ import { SearchTraceIDResponse, SearchTraceQueryResponse } from '../model/api-types';
3
+ export declare const MOCK_TRACE_RESPONSE: SearchTraceIDResponse;
4
+ export declare const MOCK_TRACE_RESPONSE_SMALL: SearchTraceIDResponse;
5
+ export declare const MOCK_SEARCH_RESPONSE_VPARQUET3: SearchTraceQueryResponse;
6
+ export declare const MOCK_SEARCH_RESPONSE_VPARQUET4: SearchTraceQueryResponse;
7
+ export declare const MOCK_TRACE_DATA_SEARCHRESULT: TraceData;
8
+ export declare const MOCK_TRACE_DATA_TRACE: TraceData;
5
9
  //# sourceMappingURL=mock-data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mock-data.d.ts","sourceRoot":"","sources":["../../src/test/mock-data.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAEhE,eAAO,MAAM,kCAAkC,EAAE,0BAy0BhD,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,SAY7B,CAAC"}
1
+ {"version":3,"file":"mock-data.d.ts","sourceRoot":"","sources":["../../src/test/mock-data.ts"],"names":[],"mappings":"AAaA,OAAO,EAAQ,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAErF,eAAO,MAAM,mBAAmB,EAAE,qBA2gEjC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,qBA+IvC,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,wBAkD5C,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,wBAmE5C,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,SA4B1C,CAAC;AA6JF,eAAO,MAAM,qBAAqB,EAAE,SAKnC,CAAC"}