Skip to content

Commit 2732991

Browse files
Copilotphrocker
andcommitted
Complete Jaeger OpenTelemetry UI feature with mock data and tests
Co-authored-by: phrocker <[email protected]>
1 parent 2377a88 commit 2732991

File tree

4 files changed

+238
-1
lines changed

4 files changed

+238
-1
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package io.sentrius.sso.controllers.api;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
import java.time.Instant;
9+
import java.util.*;
10+
11+
/**
12+
* Mock Jaeger API for testing telemetry UI when actual Jaeger is not available.
13+
* This controller provides sample trace data for demonstration purposes.
14+
*/
15+
@RestController
16+
@RequestMapping("/mock/jaeger/api")
17+
public class MockJaegerApiController {
18+
19+
@GetMapping("/services")
20+
public ResponseEntity<?> getMockServices() {
21+
Map<String, Object> response = new HashMap<>();
22+
List<String> services = Arrays.asList(
23+
"sentrius-api",
24+
"sentrius-dataplane",
25+
"sentrius-agent-proxy",
26+
"sentrius-integration-proxy"
27+
);
28+
response.put("data", services);
29+
return ResponseEntity.ok(response);
30+
}
31+
32+
@GetMapping("/traces")
33+
public ResponseEntity<?> getMockTraces() {
34+
Map<String, Object> response = new HashMap<>();
35+
List<Map<String, Object>> traces = new ArrayList<>();
36+
37+
// Create sample trace 1
38+
Map<String, Object> trace1 = createSampleTrace(
39+
"1234567890abcdef",
40+
"sentrius-api",
41+
Arrays.asList("HTTP GET /sso/v1/dashboard", "Database Query", "Cache Lookup"),
42+
150000 // 150ms
43+
);
44+
45+
// Create sample trace 2
46+
Map<String, Object> trace2 = createSampleTrace(
47+
"fedcba0987654321",
48+
"sentrius-api",
49+
Arrays.asList("HTTP POST /api/v1/users", "User Validation", "Database Insert", "Send Notification"),
50+
320000 // 320ms
51+
);
52+
53+
traces.add(trace1);
54+
traces.add(trace2);
55+
56+
response.put("data", traces);
57+
return ResponseEntity.ok(response);
58+
}
59+
60+
private Map<String, Object> createSampleTrace(String traceId, String serviceName, List<String> operations, long totalDuration) {
61+
Map<String, Object> trace = new HashMap<>();
62+
trace.put("traceID", traceId);
63+
64+
List<Map<String, Object>> spans = new ArrayList<>();
65+
long startTime = Instant.now().toEpochMilli() * 1000; // Convert to microseconds
66+
long currentTime = startTime;
67+
68+
for (int i = 0; i < operations.size(); i++) {
69+
Map<String, Object> span = new HashMap<>();
70+
span.put("spanID", String.format("%016x", i + 1));
71+
span.put("operationName", operations.get(i));
72+
span.put("startTime", currentTime);
73+
74+
long duration = totalDuration / operations.size();
75+
span.put("duration", duration);
76+
77+
// Create process info
78+
Map<String, Object> process = new HashMap<>();
79+
process.put("serviceName", serviceName);
80+
span.put("process", process);
81+
82+
// Add references for child spans
83+
if (i > 0) {
84+
List<Map<String, Object>> references = new ArrayList<>();
85+
Map<String, Object> ref = new HashMap<>();
86+
ref.put("refType", "CHILD_OF");
87+
ref.put("spanID", String.format("%016x", i)); // Reference parent
88+
references.add(ref);
89+
span.put("references", references);
90+
} else {
91+
span.put("references", new ArrayList<>());
92+
}
93+
94+
spans.add(span);
95+
currentTime += duration;
96+
}
97+
98+
trace.put("spans", spans);
99+
return trace;
100+
}
101+
}

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ <h5 class="card-title mb-0">Query Jaeger Traces</h5>
8181
</div>
8282
<button type="submit" class="btn btn-primary">Search Traces</button>
8383
<button type="button" class="btn btn-secondary" onclick="clearForm()">Clear</button>
84+
<button type="button" class="btn btn-info" onclick="loadMockData()">Load Mock Data</button>
8485
</form>
8586
</div>
8687
</div>
@@ -368,6 +369,28 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
368369
errorDiv.style.display = 'block';
369370
}
370371

372+
async function loadMockData() {
373+
document.getElementById('loading').style.display = 'block';
374+
document.getElementById('results-section').style.display = 'none';
375+
document.getElementById('error-display').style.display = 'none';
376+
377+
try {
378+
const response = await fetch('/mock/jaeger/api/traces');
379+
if (response.ok) {
380+
const data = await response.json();
381+
currentTraces = data.data || [];
382+
displayResults({traces: currentTraces, status: 'success', count: currentTraces.length});
383+
} else {
384+
showError('Failed to load mock data');
385+
}
386+
} catch (error) {
387+
console.error('Error loading mock data:', error);
388+
showError('Error loading mock data: ' + error.message);
389+
} finally {
390+
document.getElementById('loading').style.display = 'none';
391+
}
392+
}
393+
371394
// Load services on page load
372395
document.addEventListener('DOMContentLoaded', function() {
373396
loadServices();
@@ -396,7 +419,26 @@ <h5 class="card-title mb-0">Call Graph Visualization</h5>
396419
}
397420
}
398421
} catch (error) {
399-
console.warn('Could not load services from Jaeger:', error);
422+
console.warn('Could not load services from Jaeger, trying mock data:', error);
423+
// Try to load mock services
424+
try {
425+
const mockResponse = await fetch('/mock/jaeger/api/services');
426+
if (mockResponse.ok) {
427+
const mockData = await mockResponse.json();
428+
const serviceSelect = document.getElementById('service-name');
429+
430+
if (mockData.data && Array.isArray(mockData.data)) {
431+
mockData.data.forEach(service => {
432+
const option = document.createElement('option');
433+
option.value = service;
434+
option.textContent = service + ' (mock)';
435+
serviceSelect.appendChild(option);
436+
});
437+
}
438+
}
439+
} catch (mockError) {
440+
console.warn('Could not load mock services either:', mockError);
441+
}
400442
}
401443
}
402444
</script>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package io.sentrius.sso.controllers.api;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.http.ResponseEntity;
5+
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
class MockJaegerApiControllerTest {
12+
13+
private MockJaegerApiController mockController = new MockJaegerApiController();
14+
15+
@Test
16+
void testGetMockServices() {
17+
ResponseEntity<?> response = mockController.getMockServices();
18+
19+
assertEquals(200, response.getStatusCode().value());
20+
assertNotNull(response.getBody());
21+
22+
Map<String, Object> body = (Map<String, Object>) response.getBody();
23+
assertTrue(body.containsKey("data"));
24+
25+
List<String> services = (List<String>) body.get("data");
26+
assertEquals(4, services.size());
27+
assertTrue(services.contains("sentrius-api"));
28+
assertTrue(services.contains("sentrius-dataplane"));
29+
}
30+
31+
@Test
32+
void testGetMockTraces() {
33+
ResponseEntity<?> response = mockController.getMockTraces();
34+
35+
assertEquals(200, response.getStatusCode().value());
36+
assertNotNull(response.getBody());
37+
38+
Map<String, Object> body = (Map<String, Object>) response.getBody();
39+
assertTrue(body.containsKey("data"));
40+
41+
List<Map<String, Object>> traces = (List<Map<String, Object>>) body.get("data");
42+
assertEquals(2, traces.size());
43+
44+
// Verify trace structure
45+
Map<String, Object> trace = traces.get(0);
46+
assertTrue(trace.containsKey("traceID"));
47+
assertTrue(trace.containsKey("spans"));
48+
49+
List<Map<String, Object>> spans = (List<Map<String, Object>>) trace.get("spans");
50+
assertTrue(spans.size() > 0);
51+
52+
// Verify span structure
53+
Map<String, Object> span = spans.get(0);
54+
assertTrue(span.containsKey("spanID"));
55+
assertTrue(span.containsKey("operationName"));
56+
assertTrue(span.containsKey("startTime"));
57+
assertTrue(span.containsKey("duration"));
58+
assertTrue(span.containsKey("process"));
59+
}
60+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.sentrius.sso.controllers.view;
2+
3+
import io.sentrius.sso.core.config.SystemOptions;
4+
import io.sentrius.sso.core.services.ErrorOutputService;
5+
import io.sentrius.sso.core.services.UserService;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.ExtendWith;
8+
import org.mockito.InjectMocks;
9+
import org.mockito.Mock;
10+
import org.mockito.junit.jupiter.MockitoExtension;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
14+
@ExtendWith(MockitoExtension.class)
15+
class TelemetryControllerTest {
16+
17+
@Mock
18+
private UserService userService;
19+
20+
@Mock
21+
private SystemOptions systemOptions;
22+
23+
@Mock
24+
private ErrorOutputService errorOutputService;
25+
26+
@InjectMocks
27+
private TelemetryController telemetryController;
28+
29+
@Test
30+
void testTelemetryPageMapping() {
31+
String result = telemetryController.telemetry();
32+
assertEquals("sso/telemetry", result);
33+
}
34+
}

0 commit comments

Comments
 (0)