Skip to content

Commit ef71b6a

Browse files
Copilotphrocker
andcommitted
Fix telemetry UI layout, pagination, and default service issues
Co-authored-by: phrocker <[email protected]>
1 parent 2c94894 commit ef71b6a

File tree

3 files changed

+185
-23
lines changed

3 files changed

+185
-23
lines changed

api/src/main/java/io/sentrius/sso/controllers/api/TelemetryApiController.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ public ResponseEntity<?> getTraces(
4040
@RequestParam(defaultValue = "1h") String lookback,
4141
@RequestParam(required = false) Long minDuration,
4242
@RequestParam(required = false) Long maxDuration,
43-
@RequestParam(required = false) String tags
43+
@RequestParam(required = false) String tags,
44+
@RequestParam(defaultValue = "20") int limit,
45+
@RequestParam(defaultValue = "0") int start
4446
) {
4547
try {
46-
String jaegerApiUrl = buildJaegerApiUrl(service, operation, lookback, minDuration, maxDuration, tags);
48+
String jaegerApiUrl = buildJaegerApiUrl(service, operation, lookback, minDuration, maxDuration, tags, limit, start);
4749
log.info("Querying Jaeger at: {}", jaegerApiUrl);
4850

4951
HttpHeaders headers = new HttpHeaders();
@@ -65,6 +67,9 @@ public ResponseEntity<?> getTraces(
6567
result.put("traces", processedTraces);
6668
result.put("status", "success");
6769
result.put("count", processedTraces.size());
70+
result.put("limit", limit);
71+
result.put("start", start);
72+
result.put("hasMore", processedTraces.size() >= limit); // Indicate if there might be more data
6873

6974
return ResponseEntity.ok(result);
7075
} else {
@@ -132,12 +137,14 @@ public ResponseEntity<?> getTrace(@PathVariable String traceId) {
132137
}
133138

134139
private String buildJaegerApiUrl(String service, String operation, String lookback,
135-
Long minDuration, Long maxDuration, String tags) {
140+
Long minDuration, Long maxDuration, String tags, int limit, int start) {
136141
StringBuilder url = new StringBuilder(jaegerQueryUrl + "/api/traces?");
137142

138-
if (service != null && !service.isEmpty()) {
139-
url.append("service=").append(service).append("&");
143+
// Default to sentrius-api if no service is provided to prevent 500 errors
144+
if (service == null || service.isEmpty()) {
145+
service = "sentrius-api";
140146
}
147+
url.append("service=").append(service).append("&");
141148

142149
if (operation != null && !operation.isEmpty()) {
143150
url.append("operation=").append(operation).append("&");
@@ -157,8 +164,9 @@ private String buildJaegerApiUrl(String service, String operation, String lookba
157164
url.append("tags=").append(tags).append("&");
158165
}
159166

160-
// Add limit to prevent too many results
161-
url.append("limit=100");
167+
// Add pagination parameters
168+
url.append("limit=").append(Math.min(limit, 100)).append("&"); // Cap at 100
169+
url.append("start=").append(start);
162170

163171
return url.toString();
164172
}

api/src/main/resources/templates/sso/telemetry.html

Lines changed: 140 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,24 @@
1919
.trace-result:hover { background-color: #3c3c3c; }
2020
.trace-result.selected { background-color: #0d6efd; }
2121
.span-info { font-size: 0.9em; color: #888; }
22+
23+
/* Pagination styles */
24+
#pagination-controls button:disabled {
25+
opacity: 0.5;
26+
cursor: not-allowed;
27+
}
28+
.sidebar {
29+
min-height: 100vh;
30+
position: sticky;
31+
top: 0;
32+
}
2233
</style>
2334
</head>
2435
<body>
2536
<div class="container-fluid">
26-
<div class="row">
37+
<div class="row flex-nowrap">
2738
<!-- Include sidebar -->
28-
<div th:replace="~{fragments/sidebar}"></div>
39+
<div th:replace="~{fragments/sidebar}" class="col-auto sidebar" style="width: 180px;"></div>
2940

3041
<!-- Main content -->
3142
<div class="col py-3">
@@ -43,8 +54,8 @@ <h5 class="card-title mb-0">Query Jaeger Traces</h5>
4354
<div class="col-md-4">
4455
<label for="service-name" class="form-label">Service Name</label>
4556
<select class="form-control" id="service-name" name="service">
57+
<option value="sentrius-api" selected>sentrius-api</option>
4658
<option value="">All Services</option>
47-
<option value="sentrius-api">sentrius-api</option>
4859
<option value="sentrius-dataplane">sentrius-dataplane</option>
4960
<option value="sentrius-agent-proxy">sentrius-agent-proxy</option>
5061
</select>
@@ -79,6 +90,21 @@ <h5 class="card-title mb-0">Query Jaeger Traces</h5>
7990
<input type="text" class="form-control" id="tags" name="tags" placeholder="e.g. error=true, user.id=123">
8091
</div>
8192
</div>
93+
<div class="row mb-3">
94+
<div class="col-md-6">
95+
<label for="limit" class="form-label">Results per page</label>
96+
<select class="form-control" id="limit" name="limit">
97+
<option value="10">10</option>
98+
<option value="20" selected>20</option>
99+
<option value="50">50</option>
100+
<option value="100">100</option>
101+
</select>
102+
</div>
103+
<div class="col-md-6">
104+
<label for="start" class="form-label">Start from result</label>
105+
<input type="number" class="form-control" id="start" name="start" value="0" min="0" step="1">
106+
</div>
107+
</div>
82108
<button type="submit" class="btn btn-primary">Search Traces</button>
83109
<button type="button" class="btn btn-secondary" onclick="clearForm()">Clear</button>
84110
<button type="button" class="btn btn-info" onclick="loadMockData()">Load Mock Data</button>
@@ -101,11 +127,24 @@ <h5 class="card-title mb-0">Query Jaeger Traces</h5>
101127
<div id="results-section" style="display: none;">
102128
<!-- Trace List -->
103129
<div class="card mb-4">
104-
<div class="card-header">
130+
<div class="card-header d-flex justify-content-between align-items-center">
105131
<h5 class="card-title mb-0">Trace Results <span id="trace-count" class="badge bg-secondary">0</span></h5>
132+
<div id="pagination-info" class="text-muted" style="display: none;">
133+
<span id="result-range"></span>
134+
</div>
106135
</div>
107136
<div class="card-body">
108137
<div id="trace-list"></div>
138+
<!-- Pagination Controls -->
139+
<div id="pagination-controls" class="d-flex justify-content-between align-items-center mt-3" style="display: none;">
140+
<button id="prev-page" class="btn btn-outline-primary" onclick="previousPage()">
141+
<i class="fas fa-chevron-left"></i> Previous
142+
</button>
143+
<span id="page-info" class="text-muted"></span>
144+
<button id="next-page" class="btn btn-outline-primary" onclick="nextPage()">
145+
Next <i class="fas fa-chevron-right"></i>
146+
</button>
147+
</div>
109148
</div>
110149
</div>
111150

@@ -127,6 +166,10 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
127166
<script>
128167
let cyGraph = null;
129168
let currentTraces = [];
169+
let currentQueryParams = {};
170+
let currentStart = 0;
171+
let currentLimit = 20;
172+
let hasMoreResults = false;
130173

131174
document.getElementById('trace-query-form').addEventListener('submit', function(e) {
132175
e.preventDefault();
@@ -137,6 +180,18 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
137180
document.getElementById('trace-query-form').reset();
138181
document.getElementById('results-section').style.display = 'none';
139182
document.getElementById('error-display').style.display = 'none';
183+
184+
// Reset to default values
185+
document.getElementById('service-name').value = 'sentrius-api';
186+
document.getElementById('limit').value = '20';
187+
document.getElementById('start').value = '0';
188+
189+
// Reset pagination state
190+
currentStart = 0;
191+
currentLimit = 20;
192+
hasMoreResults = false;
193+
currentQueryParams = {};
194+
140195
if (cyGraph) {
141196
cyGraph.destroy();
142197
cyGraph = null;
@@ -155,6 +210,15 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
155210
}
156211
}
157212

213+
// Store current query params for pagination
214+
currentQueryParams = Object.fromEntries(params.entries());
215+
currentStart = parseInt(currentQueryParams.start || '0');
216+
currentLimit = parseInt(currentQueryParams.limit || '20');
217+
218+
await executeSearch(params);
219+
}
220+
221+
async function executeSearch(params) {
158222
// Show loading
159223
document.getElementById('loading').style.display = 'block';
160224
document.getElementById('results-section').style.display = 'none';
@@ -168,6 +232,7 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
168232

169233
const data = await response.json();
170234
currentTraces = data.traces || [];
235+
hasMoreResults = data.hasMore || false;
171236
displayResults(data);
172237
} catch (error) {
173238
console.error('Error fetching traces:', error);
@@ -181,6 +246,9 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
181246
const traceCount = data.traces ? data.traces.length : 0;
182247
document.getElementById('trace-count').textContent = traceCount;
183248

249+
// Update pagination info
250+
updatePaginationInfo(data);
251+
184252
// Display trace list
185253
const traceList = document.getElementById('trace-list');
186254
if (traceCount === 0) {
@@ -197,6 +265,50 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
197265
document.getElementById('results-section').style.display = 'block';
198266
}
199267

268+
function updatePaginationInfo(data) {
269+
const start = data.start || 0;
270+
const limit = data.limit || 20;
271+
const count = data.count || 0;
272+
const hasMore = data.hasMore || false;
273+
274+
// Update range display
275+
const endRange = start + count;
276+
document.getElementById('result-range').textContent =
277+
count > 0 ? `Showing ${start + 1}-${endRange}` : 'No results';
278+
document.getElementById('pagination-info').style.display = count > 0 ? 'block' : 'none';
279+
280+
// Update pagination controls
281+
const prevBtn = document.getElementById('prev-page');
282+
const nextBtn = document.getElementById('next-page');
283+
const pageInfo = document.getElementById('page-info');
284+
285+
prevBtn.disabled = start === 0;
286+
nextBtn.disabled = !hasMore;
287+
288+
const currentPage = Math.floor(start / limit) + 1;
289+
pageInfo.textContent = `Page ${currentPage}`;
290+
291+
document.getElementById('pagination-controls').style.display = count > 0 ? 'flex' : 'none';
292+
}
293+
294+
async function previousPage() {
295+
if (currentStart > 0) {
296+
currentStart = Math.max(0, currentStart - currentLimit);
297+
const params = new URLSearchParams(currentQueryParams);
298+
params.set('start', currentStart.toString());
299+
await executeSearch(params);
300+
}
301+
}
302+
303+
async function nextPage() {
304+
if (hasMoreResults) {
305+
currentStart += currentLimit;
306+
const params = new URLSearchParams(currentQueryParams);
307+
params.set('start', currentStart.toString());
308+
await executeSearch(params);
309+
}
310+
}
311+
200312
function createTraceHtml(trace) {
201313
const duration = trace.duration ? (trace.duration / 1000).toFixed(2) + ' ms' : 'Unknown';
202314
const startTime = trace.startTime ? new Date(trace.startTime / 1000).toLocaleString() : 'Unknown';
@@ -402,21 +514,28 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
402514
if (response.ok) {
403515
const data = await response.json();
404516
const serviceSelect = document.getElementById('service-name');
517+
const currentValue = serviceSelect.value; // Preserve current selection
405518

406-
// Clear existing options except "All Services"
407-
while (serviceSelect.children.length > 1) {
519+
// Clear existing options except defaults
520+
while (serviceSelect.children.length > 2) {
408521
serviceSelect.removeChild(serviceSelect.lastChild);
409522
}
410523

411524
// Add services from Jaeger
412525
if (data.data && Array.isArray(data.data)) {
413526
data.data.forEach(service => {
414-
const option = document.createElement('option');
415-
option.value = service;
416-
option.textContent = service;
417-
serviceSelect.appendChild(option);
527+
// Skip if already exists in defaults
528+
if (!['sentrius-api', ''].includes(service)) {
529+
const option = document.createElement('option');
530+
option.value = service;
531+
option.textContent = service;
532+
serviceSelect.appendChild(option);
533+
}
418534
});
419535
}
536+
537+
// Restore selection
538+
serviceSelect.value = currentValue;
420539
}
421540
} catch (error) {
422541
console.warn('Could not load services from Jaeger, trying mock data:', error);
@@ -426,15 +545,22 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
426545
if (mockResponse.ok) {
427546
const mockData = await mockResponse.json();
428547
const serviceSelect = document.getElementById('service-name');
548+
const currentValue = serviceSelect.value;
429549

430550
if (mockData.data && Array.isArray(mockData.data)) {
431551
mockData.data.forEach(service => {
432-
const option = document.createElement('option');
433-
option.value = service;
434-
option.textContent = service + ' (mock)';
435-
serviceSelect.appendChild(option);
552+
// Skip if already exists in defaults
553+
if (!['sentrius-api', ''].includes(service)) {
554+
const option = document.createElement('option');
555+
option.value = service;
556+
option.textContent = service + ' (mock)';
557+
serviceSelect.appendChild(option);
558+
}
436559
});
437560
}
561+
562+
// Restore selection
563+
serviceSelect.value = currentValue;
438564
}
439565
} catch (mockError) {
440566
console.warn('Could not load mock services either:', mockError);

api/src/test/java/io/sentrius/sso/controllers/api/TelemetryApiControllerTest.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void testGetTracesHandlesInvalidJaegerUrl() {
3434
ReflectionTestUtils.setField(telemetryApiController, "jaegerQueryUrl", "invalid-url");
3535

3636
ResponseEntity<?> response = telemetryApiController.getTraces(
37-
"test-service", null, "1h", null, null, null
37+
"test-service", null, "1h", null, null, null, 20, 0
3838
);
3939

4040
assertEquals(500, response.getStatusCode().value());
@@ -48,7 +48,7 @@ void testGetTracesWithValidParameters() {
4848

4949
// This test will fail to connect to Jaeger, but should not throw exception
5050
ResponseEntity<?> response = telemetryApiController.getTraces(
51-
"sentrius-api", "test-operation", "1h", 1000L, 10000L, "error=true"
51+
"sentrius-api", "test-operation", "1h", 1000L, 10000L, "error=true", 20, 0
5252
);
5353

5454
// Should return 500 since we can't connect to real Jaeger, but shouldn't crash
@@ -74,4 +74,32 @@ void testGetTraceByIdWithInvalidUrl() {
7474
assertEquals(500, response.getStatusCode().value());
7575
assertNotNull(response.getBody());
7676
}
77+
78+
@Test
79+
void testGetTracesDefaultsToSentriusApiService() {
80+
// Set a mock Jaeger URL
81+
ReflectionTestUtils.setField(telemetryApiController, "jaegerQueryUrl", "http://localhost:16686");
82+
83+
// Test with null/empty service parameter - should default to sentrius-api
84+
ResponseEntity<?> response = telemetryApiController.getTraces(
85+
null, null, "1h", null, null, null, 20, 0
86+
);
87+
88+
// Should return 500 since we can't connect to real Jaeger, but the method should handle null service
89+
assertEquals(500, response.getStatusCode().value());
90+
}
91+
92+
@Test
93+
void testGetTracesWithPaginationParameters() {
94+
// Set a mock Jaeger URL
95+
ReflectionTestUtils.setField(telemetryApiController, "jaegerQueryUrl", "http://localhost:16686");
96+
97+
// Test with pagination parameters
98+
ResponseEntity<?> response = telemetryApiController.getTraces(
99+
"sentrius-api", null, "1h", null, null, null, 50, 100
100+
);
101+
102+
// Should return 500 since we can't connect to real Jaeger, but shouldn't crash with pagination
103+
assertEquals(500, response.getStatusCode().value());
104+
}
77105
}

0 commit comments

Comments
 (0)