@perses-dev/tempo-plugin 0.52.0 → 0.53.0

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 (181) hide show
  1. package/__mf/js/Tempo.2f6d49a1.js +5 -0
  2. package/__mf/js/async/1101.0dae4724.js +73 -0
  3. package/__mf/js/async/1576.83b7de56.js +1 -0
  4. package/__mf/js/async/1964.e6c5b93b.js +2 -0
  5. package/__mf/js/async/2292.75e9aa11.js +2 -0
  6. package/__mf/js/async/2913.f73e6635.js +7 -0
  7. package/__mf/js/async/2913.f73e6635.js.LICENSE.txt +21 -0
  8. package/__mf/js/async/2981.8fe4ed12.js +2 -0
  9. package/__mf/js/async/3224.ce173388.js +1 -0
  10. package/__mf/js/async/3863.8d56ecec.js +2 -0
  11. package/__mf/js/async/3960.401ff0b0.js +2 -0
  12. package/__mf/js/async/3980.4d5490b2.js +2 -0
  13. package/__mf/js/async/4075.4c5ac93a.js +1 -0
  14. package/__mf/js/async/4238.7962a7a1.js +1 -0
  15. package/__mf/js/async/4269.0cfaf9bb.js +2 -0
  16. package/__mf/js/async/4368.bd6fd0e7.js +2 -0
  17. package/__mf/js/async/4368.bd6fd0e7.js.LICENSE.txt +15 -0
  18. package/__mf/js/async/4421.14238d27.js +1 -0
  19. package/__mf/js/async/4535.7c92d3fd.js +1 -0
  20. package/__mf/js/async/5214.c44cbfe5.js +1 -0
  21. package/__mf/js/async/5266.1c520126.js +10 -0
  22. package/__mf/js/async/5409.676a5f3f.js +1 -0
  23. package/__mf/js/async/5790.b4d4134d.js +1 -0
  24. package/__mf/js/async/5876.47f40562.js +2 -0
  25. package/__mf/js/async/{7832.9f2a70d0.js.LICENSE.txt → 5876.47f40562.js.LICENSE.txt} +1 -23
  26. package/__mf/js/async/5981.c6edce96.js +2 -0
  27. package/__mf/js/async/6292.463b4f49.js +1 -0
  28. package/__mf/js/async/6333.367d6758.js +2 -0
  29. package/__mf/js/async/6495.eae3d4b4.js +1 -0
  30. package/__mf/js/async/6527.5341d09f.js +2 -0
  31. package/__mf/js/async/6751.03514b88.js +1 -0
  32. package/__mf/js/async/6770.d9c238f2.js +1 -0
  33. package/__mf/js/async/694.c2c37771.js +1 -0
  34. package/__mf/js/async/7127.a0877987.js +38 -0
  35. package/__mf/js/async/7376.588b7a17.js +1 -0
  36. package/__mf/js/async/738.93d35dc9.js +1 -0
  37. package/__mf/js/async/7740.0500bfc6.js +1 -0
  38. package/__mf/js/async/8216.2f92a883.js +1 -0
  39. package/__mf/js/async/8488.6c9a25e4.js +1 -0
  40. package/__mf/js/async/8597.d5ba4ca7.js +1 -0
  41. package/__mf/js/async/8930.dee9777e.js +1 -0
  42. package/__mf/js/async/9173.69dc268d.js +2 -0
  43. package/__mf/js/async/9314.4b8565a0.js +2 -0
  44. package/__mf/js/async/9368.f0418d24.js +101 -0
  45. package/__mf/js/async/__federation_expose_TempoDatasource.fa3e24f9.js +2 -0
  46. package/__mf/js/async/__federation_expose_TempoExplorer.9949b25e.js +2 -0
  47. package/__mf/js/async/__federation_expose_TempoTraceQuery.13537765.js +1 -0
  48. package/__mf/js/async/lib-router.312ca028.js +2 -0
  49. package/__mf/js/main.edbb7a5a.js +5 -0
  50. package/lib/cjs/components/AttributeFilters.js +274 -0
  51. package/lib/cjs/components/TraceQLEditor.js +9 -3
  52. package/lib/cjs/components/complete.js +29 -13
  53. package/lib/cjs/components/filter/filter.js +25 -0
  54. package/lib/cjs/components/filter/filter_to_traceql.js +66 -0
  55. package/lib/cjs/components/filter/index.js +32 -0
  56. package/lib/cjs/components/filter/traceql_to_filter.js +113 -0
  57. package/lib/cjs/components/index.js +1 -0
  58. package/lib/cjs/explore/TempoExplorer.js +25 -12
  59. package/lib/cjs/explore/links.js +77 -0
  60. package/lib/cjs/plugins/tempo-trace-query/TempoTraceQueryEditor.js +94 -30
  61. package/lib/cjs/plugins/tempo-trace-query/get-trace-data.js +8 -0
  62. package/lib/cjs/plugins/tempo-trace-query/query-editor-model.js +3 -44
  63. package/lib/components/AttributeFilters.d.ts +9 -0
  64. package/lib/components/AttributeFilters.d.ts.map +1 -0
  65. package/lib/components/AttributeFilters.js +261 -0
  66. package/lib/components/AttributeFilters.js.map +1 -0
  67. package/lib/components/TraceQLEditor.d.ts +3 -3
  68. package/lib/components/TraceQLEditor.d.ts.map +1 -1
  69. package/lib/components/TraceQLEditor.js +9 -3
  70. package/lib/components/TraceQLEditor.js.map +1 -1
  71. package/lib/components/complete.d.ts +10 -1
  72. package/lib/components/complete.d.ts.map +1 -1
  73. package/lib/components/complete.js +26 -7
  74. package/lib/components/complete.js.map +1 -1
  75. package/lib/components/filter/filter.d.ts +17 -0
  76. package/lib/components/filter/filter.d.ts.map +1 -0
  77. package/lib/components/filter/filter.js +17 -0
  78. package/lib/components/filter/filter.js.map +1 -0
  79. package/lib/components/filter/filter_to_traceql.d.ts +9 -0
  80. package/lib/components/filter/filter_to_traceql.d.ts.map +1 -0
  81. package/lib/components/filter/filter_to_traceql.js +63 -0
  82. package/lib/components/filter/filter_to_traceql.js.map +1 -0
  83. package/lib/components/filter/index.d.ts +4 -0
  84. package/lib/components/filter/index.d.ts.map +1 -0
  85. package/lib/components/filter/index.js +17 -0
  86. package/lib/components/filter/index.js.map +1 -0
  87. package/lib/components/filter/traceql_to_filter.d.ts +9 -0
  88. package/lib/components/filter/traceql_to_filter.d.ts.map +1 -0
  89. package/lib/components/filter/traceql_to_filter.js +110 -0
  90. package/lib/components/filter/traceql_to_filter.js.map +1 -0
  91. package/lib/components/index.d.ts +1 -0
  92. package/lib/components/index.d.ts.map +1 -1
  93. package/lib/components/index.js +1 -0
  94. package/lib/components/index.js.map +1 -1
  95. package/lib/explore/TempoExplorer.d.ts.map +1 -1
  96. package/lib/explore/TempoExplorer.js +25 -12
  97. package/lib/explore/TempoExplorer.js.map +1 -1
  98. package/lib/explore/links.d.ts +3 -0
  99. package/lib/explore/links.d.ts.map +1 -0
  100. package/lib/explore/links.js +63 -0
  101. package/lib/explore/links.js.map +1 -0
  102. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.d.ts +6 -0
  103. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.d.ts.map +1 -1
  104. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.js +87 -31
  105. package/lib/plugins/tempo-trace-query/TempoTraceQueryEditor.js.map +1 -1
  106. package/lib/plugins/tempo-trace-query/get-trace-data.js +8 -0
  107. package/lib/plugins/tempo-trace-query/get-trace-data.js.map +1 -1
  108. package/lib/plugins/tempo-trace-query/query-editor-model.d.ts +0 -9
  109. package/lib/plugins/tempo-trace-query/query-editor-model.d.ts.map +1 -1
  110. package/lib/plugins/tempo-trace-query/query-editor-model.js +0 -35
  111. package/lib/plugins/tempo-trace-query/query-editor-model.js.map +1 -1
  112. package/mf-manifest.json +104 -265
  113. package/mf-stats.json +112 -270
  114. package/package.json +6 -6
  115. package/__mf/js/1096.c549c391.js +0 -5
  116. package/__mf/js/Tempo.e7d268a6.js +0 -5
  117. package/__mf/js/async/1465.21c847e0.js +0 -1
  118. package/__mf/js/async/1540.089c4f28.js +0 -74
  119. package/__mf/js/async/1620.45989def.js +0 -2
  120. package/__mf/js/async/1964.75933dd4.js +0 -2
  121. package/__mf/js/async/2114.28503adb.js +0 -1
  122. package/__mf/js/async/2823.df67fd4b.js +0 -2
  123. package/__mf/js/async/3044.8b419ccf.js +0 -1
  124. package/__mf/js/async/3090.90251187.js +0 -2
  125. package/__mf/js/async/3224.8d499a63.js +0 -1
  126. package/__mf/js/async/3355.8bd6f6bd.js +0 -1
  127. package/__mf/js/async/3828.d981b319.js +0 -2
  128. package/__mf/js/async/3960.2228bf7e.js +0 -2
  129. package/__mf/js/async/3980.c94e78cd.js +0 -2
  130. package/__mf/js/async/4075.4c40db9f.js +0 -1
  131. package/__mf/js/async/4238.db631f1f.js +0 -1
  132. package/__mf/js/async/4289.5e2073e0.js +0 -1
  133. package/__mf/js/async/4421.07335985.js +0 -1
  134. package/__mf/js/async/4758.cb86850e.js +0 -1
  135. package/__mf/js/async/5207.e63b049c.js +0 -2
  136. package/__mf/js/async/5214.fb1215df.js +0 -1
  137. package/__mf/js/async/5220.80e3b05e.js +0 -10
  138. package/__mf/js/async/528.2759052c.js +0 -1
  139. package/__mf/js/async/5503.6e47fa95.js +0 -1
  140. package/__mf/js/async/5790.949d8d1c.js +0 -1
  141. package/__mf/js/async/5913.d10c6185.js +0 -73
  142. package/__mf/js/async/5924.bfb4b2fd.js +0 -2
  143. package/__mf/js/async/5981.4700ddf6.js +0 -2
  144. package/__mf/js/async/6292.2481b399.js +0 -1
  145. package/__mf/js/async/6770.4b9911ea.js +0 -1
  146. package/__mf/js/async/694.91676c53.js +0 -1
  147. package/__mf/js/async/7127.ccd78bd8.js +0 -38
  148. package/__mf/js/async/7376.a69c2e5a.js +0 -1
  149. package/__mf/js/async/738.2cdddba7.js +0 -1
  150. package/__mf/js/async/7740.1ecb3732.js +0 -1
  151. package/__mf/js/async/7832.9f2a70d0.js +0 -7
  152. package/__mf/js/async/8485.434a672e.js +0 -28
  153. package/__mf/js/async/8488.d3005164.js +0 -1
  154. package/__mf/js/async/8597.07c3a890.js +0 -1
  155. package/__mf/js/async/8930.ae855fbe.js +0 -1
  156. package/__mf/js/async/9173.83562213.js +0 -2
  157. package/__mf/js/async/9478.57f45cd9.js +0 -2
  158. package/__mf/js/async/__federation_expose_TempoDatasource.4f96e206.js +0 -2
  159. package/__mf/js/async/__federation_expose_TempoExplorer.eb09c758.js +0 -2
  160. package/__mf/js/async/__federation_expose_TempoTraceQuery.4183e294.js +0 -1
  161. package/__mf/js/async/lib-router.46460b13.js +0 -2
  162. package/__mf/js/main.4498dd88.js +0 -1
  163. /package/__mf/css/async/{4758.c10cf504.css → 1576.c10cf504.css} +0 -0
  164. /package/__mf/css/async/{5207.c10cf504.css → 9314.c10cf504.css} +0 -0
  165. /package/__mf/js/async/{5913.d10c6185.js.LICENSE.txt → 1101.0dae4724.js.LICENSE.txt} +0 -0
  166. /package/__mf/js/async/{1964.75933dd4.js.LICENSE.txt → 1964.e6c5b93b.js.LICENSE.txt} +0 -0
  167. /package/__mf/js/async/{3090.90251187.js.LICENSE.txt → 2292.75e9aa11.js.LICENSE.txt} +0 -0
  168. /package/__mf/js/async/{1620.45989def.js.LICENSE.txt → 2981.8fe4ed12.js.LICENSE.txt} +0 -0
  169. /package/__mf/js/async/{2823.df67fd4b.js.LICENSE.txt → 3863.8d56ecec.js.LICENSE.txt} +0 -0
  170. /package/__mf/js/async/{3960.2228bf7e.js.LICENSE.txt → 3960.401ff0b0.js.LICENSE.txt} +0 -0
  171. /package/__mf/js/async/{3980.c94e78cd.js.LICENSE.txt → 3980.4d5490b2.js.LICENSE.txt} +0 -0
  172. /package/__mf/js/async/{3828.d981b319.js.LICENSE.txt → 4269.0cfaf9bb.js.LICENSE.txt} +0 -0
  173. /package/__mf/js/async/{5220.80e3b05e.js.LICENSE.txt → 5266.1c520126.js.LICENSE.txt} +0 -0
  174. /package/__mf/js/async/{5981.4700ddf6.js.LICENSE.txt → 5981.c6edce96.js.LICENSE.txt} +0 -0
  175. /package/__mf/js/async/{5207.e63b049c.js.LICENSE.txt → 6333.367d6758.js.LICENSE.txt} +0 -0
  176. /package/__mf/js/async/{5924.bfb4b2fd.js.LICENSE.txt → 6527.5341d09f.js.LICENSE.txt} +0 -0
  177. /package/__mf/js/async/{9173.83562213.js.LICENSE.txt → 9173.69dc268d.js.LICENSE.txt} +0 -0
  178. /package/__mf/js/async/{9478.57f45cd9.js.LICENSE.txt → 9314.4b8565a0.js.LICENSE.txt} +0 -0
  179. /package/__mf/js/async/{__federation_expose_TempoDatasource.4f96e206.js.LICENSE.txt → __federation_expose_TempoDatasource.fa3e24f9.js.LICENSE.txt} +0 -0
  180. /package/__mf/js/async/{__federation_expose_TempoExplorer.eb09c758.js.LICENSE.txt → __federation_expose_TempoExplorer.9949b25e.js.LICENSE.txt} +0 -0
  181. /package/__mf/js/async/{lib-router.46460b13.js.LICENSE.txt → lib-router.312ca028.js.LICENSE.txt} +0 -0
@@ -27,6 +27,7 @@ const _core = require("@perses-dev/core");
27
27
  const _dashboards = require("@perses-dev/dashboards");
28
28
  const _explore = require("@perses-dev/explore");
29
29
  const _pluginsystem = require("@perses-dev/plugin-system");
30
+ const _links = require("./links");
30
31
  function SearchResultsPanel({ queries }) {
31
32
  const { isFetching, isLoading, queryResults } = (0, _pluginsystem.useDataQueries)('TraceQuery');
32
33
  // no query executed, show empty panel
@@ -70,7 +71,9 @@ function SearchResultsPanel({ queries }) {
70
71
  },
71
72
  plugin: {
72
73
  kind: 'ScatterChart',
73
- spec: {}
74
+ spec: {
75
+ link: _links.linkToTrace
76
+ }
74
77
  }
75
78
  }
76
79
  }
@@ -92,7 +95,11 @@ function SearchResultsPanel({ queries }) {
92
95
  },
93
96
  plugin: {
94
97
  kind: 'TraceTable',
95
- spec: {}
98
+ spec: {
99
+ links: {
100
+ trace: _links.linkToTrace
101
+ }
102
+ }
96
103
  }
97
104
  }
98
105
  }
@@ -100,28 +107,33 @@ function SearchResultsPanel({ queries }) {
100
107
  ]
101
108
  });
102
109
  }
103
- function TracingGanttChartPanel({ queries }) {
110
+ function TracingGanttChartPanel(props) {
111
+ const { queries, selectedSpanId } = props;
112
+ const firstQuery = queries[0]?.spec.plugin.spec?.query;
104
113
  return /*#__PURE__*/ (0, _jsxruntime.jsx)(_dashboards.Panel, {
105
- panelOptions: {
106
- hideHeader: true
107
- },
108
114
  definition: {
109
115
  kind: 'Panel',
110
116
  spec: {
111
117
  queries,
112
118
  display: {
113
- name: ''
119
+ name: `Trace ${firstQuery}`
114
120
  },
115
121
  plugin: {
116
122
  kind: 'TracingGanttChart',
117
- spec: {}
123
+ spec: {
124
+ links: {
125
+ trace: _links.linkToTrace,
126
+ span: _links.linkToSpan
127
+ },
128
+ selectedSpanId
129
+ }
118
130
  }
119
131
  }
120
132
  }
121
133
  });
122
134
  }
123
135
  function TempoExplorer() {
124
- const { data: { queries = [] }, setData } = (0, _explore.useExplorerManagerContext)();
136
+ const { data: { queries = [], spanId: selectedSpanId }, setData } = (0, _explore.useExplorerManagerContext)();
125
137
  // map TraceQueryDefinition to Definition<UnknownSpec>
126
138
  const definitions = queries.length ? queries.map((query)=>{
127
139
  return {
@@ -129,8 +141,8 @@ function TempoExplorer() {
129
141
  spec: query.spec.plugin.spec
130
142
  };
131
143
  }) : [];
132
- // Cannot cast to TempoTraceQuerySpec because 'tempo-plugin' types are not accessible in @perses-dev/explore
133
- const isSingleTrace = queries.length === 1 && queries[0]?.kind === 'TraceQuery' && queries[0]?.spec.plugin.kind === 'TempoTraceQuery' && (0, _core.isValidTraceId)((queries[0]?.spec.plugin.spec).query ?? ''); // eslint-disable-line @typescript-eslint/no-explicit-any
144
+ const firstQuery = queries[0]?.spec.plugin.spec?.query;
145
+ const isSingleTrace = (0, _core.isValidTraceId)(firstQuery ?? '');
134
146
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
135
147
  gap: 2,
136
148
  sx: {
@@ -156,7 +168,8 @@ function TempoExplorer() {
156
168
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
157
169
  height: 700,
158
170
  children: isSingleTrace ? /*#__PURE__*/ (0, _jsxruntime.jsx)(TracingGanttChartPanel, {
159
- queries: queries
171
+ queries: queries,
172
+ selectedSpanId: selectedSpanId
160
173
  }) : /*#__PURE__*/ (0, _jsxruntime.jsx)(SearchResultsPanel, {
161
174
  queries: queries
162
175
  })
@@ -0,0 +1,77 @@
1
+ // Copyright 2025 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ "use strict";
14
+ Object.defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ function _export(target, all) {
18
+ for(var name in all)Object.defineProperty(target, name, {
19
+ enumerable: true,
20
+ get: all[name]
21
+ });
22
+ }
23
+ _export(exports, {
24
+ linkToSpan: function() {
25
+ return linkToSpan;
26
+ },
27
+ linkToTrace: function() {
28
+ return linkToTrace;
29
+ }
30
+ });
31
+ const linkToTraceParams = new URLSearchParams({
32
+ explorer: 'Tempo-TempoExplorer',
33
+ data: JSON.stringify({
34
+ queries: [
35
+ {
36
+ kind: 'TraceQuery',
37
+ spec: {
38
+ plugin: {
39
+ kind: 'TempoTraceQuery',
40
+ spec: {
41
+ query: 'TRACEID',
42
+ datasource: {
43
+ kind: 'TempoDatasource',
44
+ name: 'DATASOURCENAME'
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }
50
+ ]
51
+ })
52
+ });
53
+ const linkToSpanParams = new URLSearchParams({
54
+ explorer: 'Tempo-TempoExplorer',
55
+ data: JSON.stringify({
56
+ queries: [
57
+ {
58
+ kind: 'TraceQuery',
59
+ spec: {
60
+ plugin: {
61
+ kind: 'TempoTraceQuery',
62
+ spec: {
63
+ query: 'TRACEID',
64
+ datasource: {
65
+ kind: 'TempoDatasource',
66
+ name: 'DATASOURCENAME'
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ],
73
+ spanId: 'SPANID'
74
+ })
75
+ });
76
+ const linkToTrace = `/explore?${linkToTraceParams}`.replace('DATASOURCENAME', '${datasourceName}').replace('TRACEID', '${traceId}');
77
+ const linkToSpan = `/explore?${linkToSpanParams}`.replace('DATASOURCENAME', '${datasourceName}').replace('TRACEID', '${traceId}').replace('SPANID', '${spanId}');
@@ -14,9 +14,17 @@
14
14
  Object.defineProperty(exports, "__esModule", {
15
15
  value: true
16
16
  });
17
- Object.defineProperty(exports, "TempoTraceQueryEditor", {
18
- enumerable: true,
19
- get: function() {
17
+ function _export(target, all) {
18
+ for(var name in all)Object.defineProperty(target, name, {
19
+ enumerable: true,
20
+ get: all[name]
21
+ });
22
+ }
23
+ _export(exports, {
24
+ LimitSelect: function() {
25
+ return LimitSelect;
26
+ },
27
+ TempoTraceQueryEditor: function() {
20
28
  return TempoTraceQueryEditor;
21
29
  }
22
30
  });
@@ -28,26 +36,18 @@ const _immer = require("immer");
28
36
  const _react = require("react");
29
37
  const _components1 = require("../../components");
30
38
  const _temposelectors = require("../../model/tempo-selectors");
39
+ const _AttributeFilters = require("../../components/AttributeFilters");
40
+ const _filter_to_traceql = require("../../components/filter/filter_to_traceql");
41
+ const _traceql_to_filter = require("../../components/filter/traceql_to_filter");
31
42
  const _queryeditormodel = require("./query-editor-model");
32
43
  function TempoTraceQueryEditor(props) {
33
- const { onChange, value } = props;
34
- const { datasource } = value;
44
+ const { onChange, value, value: { datasource, limit }, queryHandlerSettings } = props;
35
45
  const datasourceSelectValue = datasource ?? _temposelectors.DEFAULT_TEMPO;
36
46
  const selectedDatasource = (0, _pluginsystem.useDatasourceSelectValueToSelector)(datasourceSelectValue, _temposelectors.TEMPO_DATASOURCE_KIND);
37
47
  const datasourceSelectLabelID = (0, _components.useId)('tempo-datasource-label'); // for panels with multiple queries, this component is rendered multiple times on the same page
38
48
  const { data: client } = (0, _pluginsystem.useDatasourceClient)(selectedDatasource);
39
- const { timeRange } = (0, _pluginsystem.useTimeRange)();
40
- const completionConfig = (0, _react.useMemo)(()=>{
41
- return {
42
- client,
43
- timeRange
44
- };
45
- }, [
46
- client,
47
- timeRange
48
- ]);
49
49
  const { query, handleQueryChange, handleQueryBlur } = (0, _queryeditormodel.useQueryState)(props);
50
- const { limit, handleLimitChange, handleLimitBlur, limitHasError } = (0, _queryeditormodel.useLimitState)(props);
50
+ const [showAttributeFilters, setShowAttributeFilters] = (0, _react.useState)(()=>isSimpleTraceQLQuery(query));
51
51
  const handleDatasourceChange = (next)=>{
52
52
  if ((0, _temposelectors.isTempoDatasourceSelector)(next)) {
53
53
  onChange((0, _immer.produce)(value, (draft)=>{
@@ -59,6 +59,23 @@ function TempoTraceQueryEditor(props) {
59
59
  }
60
60
  throw new Error('Got unexpected non-Tempo datasource selector');
61
61
  };
62
+ const runQuery = (newQuery)=>{
63
+ if (queryHandlerSettings?.watchQueryChanges) {
64
+ queryHandlerSettings.watchQueryChanges(newQuery);
65
+ }
66
+ onChange((0, _immer.produce)(value, (draft)=>{
67
+ draft.query = newQuery;
68
+ }));
69
+ };
70
+ const handleTraceQueryChange = (0, _react.useCallback)((e)=>{
71
+ handleQueryChange(e);
72
+ if (queryHandlerSettings?.watchQueryChanges) {
73
+ queryHandlerSettings.watchQueryChanges(e);
74
+ }
75
+ }, [
76
+ handleQueryChange,
77
+ queryHandlerSettings
78
+ ]);
62
79
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
63
80
  spacing: 2,
64
81
  children: [
@@ -77,26 +94,73 @@ function TempoTraceQueryEditor(props) {
77
94
  /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
78
95
  direction: "row",
79
96
  spacing: 2,
97
+ sx: {
98
+ alignItems: 'flex-start'
99
+ },
80
100
  children: [
81
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.TraceQLEditor, {
82
- completionConfig: completionConfig,
101
+ showAttributeFilters ? /*#__PURE__*/ (0, _jsxruntime.jsx)(_AttributeFilters.AttributeFilters, {
102
+ client: client,
103
+ query: query,
104
+ setQuery: runQuery
105
+ }) : /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.TraceQLEditor, {
106
+ client: client,
83
107
  value: query,
84
- onChange: handleQueryChange,
85
- onBlur: handleQueryBlur
108
+ onChange: handleTraceQueryChange,
109
+ onBlur: queryHandlerSettings?.runWithOnBlur ? handleQueryBlur : undefined
86
110
  }),
87
- /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.TextField, {
88
- size: "small",
89
- label: "Max Traces",
90
- value: limit,
91
- error: limitHasError,
92
- onChange: (e)=>handleLimitChange(e.target.value),
93
- onBlur: handleLimitBlur,
94
- sx: {
95
- width: '110px'
96
- }
111
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Button, {
112
+ onClick: ()=>setShowAttributeFilters(!showAttributeFilters),
113
+ children: showAttributeFilters ? 'Show query' : 'Hide query'
114
+ }),
115
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(LimitSelect, {
116
+ value: limit ?? 20,
117
+ setValue: (newLimit)=>onChange((0, _immer.produce)(value, (draft)=>{
118
+ draft.limit = newLimit;
119
+ }))
97
120
  })
98
121
  ]
99
122
  })
100
123
  ]
101
124
  });
102
125
  }
126
+ function isSimpleTraceQLQuery(query) {
127
+ // if a query can be transformed to a filter and back to the original query, we can show the attribute filter toolbar
128
+ return query == '' || (0, _filter_to_traceql.filterToTraceQL)((0, _traceql_to_filter.traceQLToFilter)(query)) === query;
129
+ }
130
+ const limitOptions = [
131
+ 20,
132
+ 50,
133
+ 100,
134
+ 500,
135
+ 1000,
136
+ 5000
137
+ ];
138
+ function LimitSelect(props) {
139
+ const { value, setValue } = props;
140
+ // the outer <Box> is required, because <FormControl> has display: inline-flex, which doesn't work with the parent <Stack> of the query editor
141
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
142
+ children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.FormControl, {
143
+ size: "small",
144
+ children: [
145
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.InputLabel, {
146
+ id: "max-traces-label",
147
+ children: "Max Traces"
148
+ }),
149
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Select, {
150
+ labelId: "max-traces-label",
151
+ id: "max-traces-select",
152
+ value: value,
153
+ label: "Max Traces",
154
+ onChange: (e)=>setValue(typeof e.target.value === 'number' ? e.target.value : parseInt(e.target.value)),
155
+ sx: {
156
+ width: 110
157
+ },
158
+ children: limitOptions.map((option)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.MenuItem, {
159
+ value: option,
160
+ children: option
161
+ }, option))
162
+ })
163
+ ]
164
+ })
165
+ });
166
+ }
@@ -113,6 +113,14 @@ function parseTraceResponse(response) {
113
113
  if (span.parentSpanId && span.parentSpanId.length != 16) {
114
114
  span.parentSpanId = base64ToHex(span.parentSpanId);
115
115
  }
116
+ for (const link of span.links ?? []){
117
+ if (link.traceId.length != 32) {
118
+ link.traceId = base64ToHex(link.traceId);
119
+ }
120
+ if (link.spanId.length != 16) {
121
+ link.spanId = base64ToHex(link.spanId);
122
+ }
123
+ }
116
124
  }
117
125
  }
118
126
  }
@@ -14,17 +14,9 @@
14
14
  Object.defineProperty(exports, "__esModule", {
15
15
  value: true
16
16
  });
17
- function _export(target, all) {
18
- for(var name in all)Object.defineProperty(target, name, {
19
- enumerable: true,
20
- get: all[name]
21
- });
22
- }
23
- _export(exports, {
24
- useLimitState: function() {
25
- return useLimitState;
26
- },
27
- useQueryState: function() {
17
+ Object.defineProperty(exports, "useQueryState", {
18
+ enumerable: true,
19
+ get: function() {
28
20
  return useQueryState;
29
21
  }
30
22
  });
@@ -59,36 +51,3 @@ function useQueryState(props) {
59
51
  handleQueryBlur
60
52
  };
61
53
  }
62
- function useLimitState(props) {
63
- const { onChange, value } = props;
64
- // TODO: reusable hook or helper util instead of duplicating from useQueryState
65
- const [limit, setLimit] = (0, _react.useState)(value.limit ? value.limit.toString() : '');
66
- const [lastSyncedLimit, setLastSyncedLimit] = (0, _react.useState)(value.limit);
67
- if (value.limit !== lastSyncedLimit) {
68
- setLimit(value.limit ? value.limit.toString() : '');
69
- setLastSyncedLimit(value.limit);
70
- }
71
- // limit must be empty or an integer > 0
72
- const limitHasError = !(limit === '' || /^[0-9]+$/.test(limit) && parseInt(limit) > 0);
73
- // Update our local state as the user types
74
- const handleLimitChange = (e)=>{
75
- setLimit(e);
76
- };
77
- // Propagate changes to the panel preview component when limit TextField is blurred
78
- const handleLimitBlur = ()=>{
79
- if (limitHasError) {
80
- return;
81
- }
82
- const limitVal = limit === '' ? undefined : parseInt(limit);
83
- setLastSyncedLimit(limitVal);
84
- onChange((0, _immer.produce)(value, (draft)=>{
85
- draft.limit = limitVal;
86
- }));
87
- };
88
- return {
89
- limit,
90
- handleLimitChange,
91
- handleLimitBlur,
92
- limitHasError
93
- };
94
- }
@@ -0,0 +1,9 @@
1
+ import { ReactElement } from 'react';
2
+ import { TempoClient } from '../model';
3
+ export interface AttributeFiltersProps {
4
+ client?: TempoClient;
5
+ query: string;
6
+ setQuery: (x: string) => void;
7
+ }
8
+ export declare function AttributeFilters(props: AttributeFiltersProps): ReactElement;
9
+ //# sourceMappingURL=AttributeFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttributeFilters.d.ts","sourceRoot":"","sources":["../../../src/components/AttributeFilters.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAoD,MAAM,OAAO,CAAC;AAOvF,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAOvC,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,CA6D3E"}
@@ -0,0 +1,261 @@
1
+ // Copyright 2025 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
+ import { useCallback, useEffect, useState } from 'react';
15
+ import { Autocomplete, Checkbox, Stack, TextField } from '@mui/material';
16
+ import { isAbsoluteTimeRange, toAbsoluteTimeRange } from '@perses-dev/core';
17
+ import { useTimeRange } from '@perses-dev/plugin-system';
18
+ import { useQuery } from '@tanstack/react-query';
19
+ import CheckboxOutline from 'mdi-material-ui/CheckboxOutline';
20
+ import CheckboxBlankOutline from 'mdi-material-ui/CheckboxBlankOutline';
21
+ import { filterToTraceQL } from './filter/filter_to_traceql';
22
+ import { traceQLToFilter } from './filter/traceql_to_filter';
23
+ import { splitByUnquotedWhitespace } from './filter/filter';
24
+ const statusOptions = [
25
+ 'unset',
26
+ 'ok',
27
+ 'error'
28
+ ];
29
+ export function AttributeFilters(props) {
30
+ const { client, query, setQuery } = props;
31
+ const filter = traceQLToFilter(query);
32
+ const setFilter = (filter)=>{
33
+ setQuery(filterToTraceQL(filter));
34
+ };
35
+ const { timeRange } = useTimeRange();
36
+ const absTimeRange = !isAbsoluteTimeRange(timeRange) ? toAbsoluteTimeRange(timeRange) : timeRange;
37
+ const startTime = Math.round(absTimeRange.start.getTime() / 1000);
38
+ const endTime = Math.round(absTimeRange.end.getTime() / 1000);
39
+ const { data: serviceNameOptions } = useTagValues(client, 'resource.service.name', filterToTraceQL({
40
+ ...filter,
41
+ serviceName: []
42
+ }), startTime, endTime);
43
+ const { data: spanNameOptions } = useTagValues(client, 'name', filterToTraceQL({
44
+ ...filter,
45
+ spanName: []
46
+ }), startTime, endTime);
47
+ return /*#__PURE__*/ _jsxs(_Fragment, {
48
+ children: [
49
+ /*#__PURE__*/ _jsx(StringAttributeFilter, {
50
+ label: "Service Name",
51
+ options: serviceNameOptions ?? [],
52
+ value: filter.serviceName,
53
+ setValue: (x)=>setFilter({
54
+ ...filter,
55
+ serviceName: x
56
+ })
57
+ }),
58
+ /*#__PURE__*/ _jsx(StringAttributeFilter, {
59
+ label: "Span Name",
60
+ options: spanNameOptions ?? [],
61
+ value: filter.spanName,
62
+ setValue: (x)=>setFilter({
63
+ ...filter,
64
+ spanName: x
65
+ })
66
+ }),
67
+ /*#__PURE__*/ _jsx(StringAttributeFilter, {
68
+ label: "Status",
69
+ width: 210,
70
+ options: statusOptions ?? [],
71
+ value: filter.status,
72
+ setValue: (x)=>setFilter({
73
+ ...filter,
74
+ status: x
75
+ })
76
+ }),
77
+ /*#__PURE__*/ _jsx(DurationAttributeFilter, {
78
+ label: "Trace Duration",
79
+ value: filter.traceDuration,
80
+ setValue: (value)=>setFilter({
81
+ ...filter,
82
+ traceDuration: value
83
+ })
84
+ }),
85
+ /*#__PURE__*/ _jsx(CustomAttributesFilter, {
86
+ label: "Custom Attributes",
87
+ value: filter.customMatchers,
88
+ setValue: (value)=>setFilter({
89
+ ...filter,
90
+ customMatchers: value
91
+ })
92
+ })
93
+ ]
94
+ });
95
+ }
96
+ const checkboxBlankIcon = /*#__PURE__*/ _jsx(CheckboxBlankOutline, {
97
+ fontSize: "small"
98
+ });
99
+ const checkedMarkedIcon = /*#__PURE__*/ _jsx(CheckboxOutline, {
100
+ fontSize: "small"
101
+ });
102
+ function StringAttributeFilter(props) {
103
+ const { label, width, options, value, setValue } = props;
104
+ return /*#__PURE__*/ _jsx(Autocomplete, {
105
+ multiple: true,
106
+ size: "small",
107
+ limitTags: 1,
108
+ disableCloseOnSelect: true,
109
+ value: value,
110
+ onChange: (_event, newValue)=>setValue(newValue),
111
+ options: options,
112
+ renderOption: (props, option, { selected })=>{
113
+ const { key, ...optionProps } = props;
114
+ return /*#__PURE__*/ _jsxs("li", {
115
+ ...optionProps,
116
+ children: [
117
+ /*#__PURE__*/ _jsx(Checkbox, {
118
+ icon: checkboxBlankIcon,
119
+ checkedIcon: checkedMarkedIcon,
120
+ style: {
121
+ marginRight: 8
122
+ },
123
+ checked: selected
124
+ }),
125
+ option
126
+ ]
127
+ }, key);
128
+ },
129
+ renderInput: (params)=>/*#__PURE__*/ _jsx(TextField, {
130
+ ...params,
131
+ label: label
132
+ }),
133
+ // Reduce the size of the chips to make space for the <input> element, the +X text and the X button to avoid a line break.
134
+ // See https://github.com/mui/material-ui/issues/38835 for more details.
135
+ slotProps: {
136
+ chip: {
137
+ sx: {
138
+ maxWidth: 'calc(100% - 45px) !important'
139
+ }
140
+ }
141
+ },
142
+ // Reduce the size of the <input> field
143
+ sx: {
144
+ width: width ?? 250,
145
+ '& input': {
146
+ minWidth: '5px !important'
147
+ }
148
+ }
149
+ });
150
+ }
151
+ function DurationAttributeFilter(props) {
152
+ const { label, value, setValue } = props;
153
+ const { min, max } = value;
154
+ return /*#__PURE__*/ _jsxs(Stack, {
155
+ direction: "row",
156
+ gap: 0.5,
157
+ children: [
158
+ /*#__PURE__*/ _jsx(DurationTextInput, {
159
+ label: `Min ${label}`,
160
+ value: min ?? '',
161
+ setValue: (min)=>setValue({
162
+ min,
163
+ max
164
+ })
165
+ }),
166
+ /*#__PURE__*/ _jsx(DurationTextInput, {
167
+ label: `Max ${label}`,
168
+ value: max ?? '',
169
+ setValue: (max)=>setValue({
170
+ min,
171
+ max
172
+ })
173
+ })
174
+ ]
175
+ });
176
+ }
177
+ const durationFormatRegex = /^([0-9]+\.)?[0-9]+(ns|ms|s|m|h)$/;
178
+ function DurationTextInput(props) {
179
+ const { label, value, setValue } = props;
180
+ return /*#__PURE__*/ _jsx(LazyTextInput, {
181
+ label: label,
182
+ size: "small",
183
+ value: value,
184
+ setValue: setValue,
185
+ validationRegex: durationFormatRegex,
186
+ validationFailedMessage: "Invalid format. Accepted format e.g. 100ms, accepted units: ns, ms, s, m, h",
187
+ sx: {
188
+ width: 150
189
+ }
190
+ });
191
+ }
192
+ function CustomAttributesFilter(props) {
193
+ const { label, value, setValue } = props;
194
+ return /*#__PURE__*/ _jsx(LazyTextInput, {
195
+ label: label,
196
+ size: "small",
197
+ placeholder: 'span.http.status_code=200 span.http.method="GET"',
198
+ value: value.join(' '),
199
+ setValue: (x)=>setValue(splitByUnquotedWhitespace(x)),
200
+ sx: {
201
+ flexGrow: 1
202
+ }
203
+ });
204
+ }
205
+ /** A <TextField> which calls props.setValue when the input field is blurred and the validation passes. */ function LazyTextInput(props) {
206
+ const { validationRegex, validationFailedMessage, value, setValue, ...otherProps } = props;
207
+ const [draftValue, setDraftValue] = useState(value);
208
+ const isValidInput = draftValue == '' || validationRegex == undefined || validationRegex.test(draftValue);
209
+ useEffect(()=>{
210
+ setDraftValue(value);
211
+ }, [
212
+ value,
213
+ setDraftValue
214
+ ]);
215
+ const handleChange = useCallback((event)=>{
216
+ setDraftValue(event.target.value);
217
+ }, []);
218
+ const handleBlur = useCallback(()=>{
219
+ if (isValidInput) {
220
+ setValue(draftValue);
221
+ }
222
+ }, [
223
+ isValidInput,
224
+ setValue,
225
+ draftValue
226
+ ]);
227
+ return /*#__PURE__*/ _jsx(TextField, {
228
+ ...otherProps,
229
+ error: !isValidInput,
230
+ helperText: isValidInput ? undefined : validationFailedMessage,
231
+ value: draftValue,
232
+ onChange: handleChange,
233
+ onBlur: handleBlur
234
+ });
235
+ }
236
+ function useTagValues(client, tag, query, start, end) {
237
+ return useQuery({
238
+ queryKey: [
239
+ 'useTagValues',
240
+ client,
241
+ tag,
242
+ query,
243
+ start,
244
+ end
245
+ ],
246
+ enabled: !!client,
247
+ queryFn: async function() {
248
+ if (!client) return;
249
+ const values = await client.searchTagValues({
250
+ tag,
251
+ q: query,
252
+ start,
253
+ end
254
+ });
255
+ return values.tagValues.map((tagValue)=>tagValue.value ?? '').sort();
256
+ },
257
+ staleTime: 60 * 1000
258
+ });
259
+ }
260
+
261
+ //# sourceMappingURL=AttributeFilters.js.map