fix: fetch actual agent/environment counts from server for tenant dashboard
All checks were successful
CI / build (push) Successful in 1m8s
CI / docker (push) Successful in 43s

The dashboard was showing hardcoded zeroes for agent and environment usage.
Now fetches real counts via M2M API from the tenant's server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-10 20:35:24 +02:00
parent cab6e409b9
commit a5445e332e
5 changed files with 51 additions and 5 deletions

View File

@@ -124,6 +124,38 @@ public class ServerApiClient {
} }
} }
/** Fetch agent count from a tenant's server. */
public int getAgentCount(String serverEndpoint) {
try {
var resp = RestClient.create().get()
.uri(serverEndpoint + "/api/v1/agents")
.header("Authorization", "Bearer " + getAccessToken())
.header("X-Cameleer-Protocol-Version", "1")
.retrieve()
.body(java.util.List.class);
return resp != null ? resp.size() : 0;
} catch (Exception e) {
log.warn("Agent count fetch failed for {}: {}", serverEndpoint, e.getMessage());
return 0;
}
}
/** Fetch environment count from a tenant's server. */
public int getEnvironmentCount(String serverEndpoint) {
try {
var resp = RestClient.create().get()
.uri(serverEndpoint + "/api/v1/admin/environments")
.header("Authorization", "Bearer " + getAccessToken())
.header("X-Cameleer-Protocol-Version", "1")
.retrieve()
.body(java.util.List.class);
return resp != null ? resp.size() : 0;
} catch (Exception e) {
log.warn("Environment count fetch failed for {}: {}", serverEndpoint, e.getMessage());
return 0;
}
}
public record ServerHealthResponse(boolean healthy, String status) {} public record ServerHealthResponse(boolean healthy, String status) {}
private synchronized String getAccessToken() { private synchronized String getAccessToken() {

View File

@@ -47,7 +47,8 @@ public class TenantPortalService {
String name, String slug, String tier, String status, String name, String slug, String tier, String status,
boolean serverHealthy, String serverStatus, String serverEndpoint, boolean serverHealthy, String serverStatus, String serverEndpoint,
String licenseTier, long licenseDaysRemaining, String licenseTier, long licenseDaysRemaining,
Map<String, Object> limits, Map<String, Object> features Map<String, Object> limits, Map<String, Object> features,
int agentCount, int environmentCount
) {} ) {}
public record LicenseData( public record LicenseData(
@@ -82,10 +83,16 @@ public class TenantPortalService {
boolean serverHealthy = false; boolean serverHealthy = false;
String serverStatus = "NO_ENDPOINT"; String serverStatus = "NO_ENDPOINT";
int agentCount = 0;
int environmentCount = 0;
if (endpoint != null && !endpoint.isBlank()) { if (endpoint != null && !endpoint.isBlank()) {
var health = serverApiClient.getHealth(endpoint); var health = serverApiClient.getHealth(endpoint);
serverHealthy = health.healthy(); serverHealthy = health.healthy();
serverStatus = health.status(); serverStatus = health.status();
if (serverHealthy) {
agentCount = serverApiClient.getAgentCount(endpoint);
environmentCount = serverApiClient.getEnvironmentCount(endpoint);
}
} }
String licenseTier = null; String licenseTier = null;
@@ -107,7 +114,7 @@ public class TenantPortalService {
tenant.getTier().name(), tenant.getStatus().name(), tenant.getTier().name(), tenant.getStatus().name(),
serverHealthy, serverStatus, endpoint, serverHealthy, serverStatus, endpoint,
licenseTier, licenseDaysRemaining, licenseTier, licenseDaysRemaining,
limits, features limits, features, agentCount, environmentCount
); );
} }

View File

@@ -104,6 +104,8 @@ class TenantPortalServiceTest {
when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant)); when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant));
when(serverApiClient.getHealth("http://server:8080")).thenReturn(new ServerHealthResponse(true, "UP")); when(serverApiClient.getHealth("http://server:8080")).thenReturn(new ServerHealthResponse(true, "UP"));
when(serverApiClient.getAgentCount("http://server:8080")).thenReturn(3);
when(serverApiClient.getEnvironmentCount("http://server:8080")).thenReturn(1);
when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license)); when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license));
var result = tenantPortalService.getDashboard(); var result = tenantPortalService.getDashboard();
@@ -119,6 +121,8 @@ class TenantPortalServiceTest {
assertThat(result.licenseDaysRemaining()).isGreaterThanOrEqualTo(29); assertThat(result.licenseDaysRemaining()).isGreaterThanOrEqualTo(29);
assertThat(result.limits()).isNotEmpty(); assertThat(result.limits()).isNotEmpty();
assertThat(result.features()).isNotEmpty(); assertThat(result.features()).isNotEmpty();
assertThat(result.agentCount()).isEqualTo(3);
assertThat(result.environmentCount()).isEqualTo(1);
} }
@Test @Test
@@ -136,6 +140,8 @@ class TenantPortalServiceTest {
assertThat(result.serverEndpoint()).isNull(); assertThat(result.serverEndpoint()).isNull();
assertThat(result.licenseTier()).isNull(); assertThat(result.licenseTier()).isNull();
assertThat(result.licenseDaysRemaining()).isZero(); assertThat(result.licenseDaysRemaining()).isZero();
assertThat(result.agentCount()).isZero();
assertThat(result.environmentCount()).isZero();
} }
// --- getLicense tests --- // --- getLicense tests ---

View File

@@ -54,9 +54,8 @@ export function TenantDashboardPage() {
const agentLimit = data.limits?.['agents'] ?? -1; const agentLimit = data.limits?.['agents'] ?? -1;
const envLimit = data.limits?.['environments'] ?? -1; const envLimit = data.limits?.['environments'] ?? -1;
// Dashboard doesn't expose usage counts directly — show limit info from license const agentUsed = data.agentCount ?? 0;
const agentUsed = 0; const envUsed = data.environmentCount ?? 0;
const envUsed = 0;
return ( return (
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 20 }}> <div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 20 }}>

View File

@@ -72,6 +72,8 @@ export interface DashboardData {
licenseDaysRemaining: number; licenseDaysRemaining: number;
limits: Record<string, number>; limits: Record<string, number>;
features: Record<string, boolean>; features: Record<string, boolean>;
agentCount: number;
environmentCount: number;
} }
export interface TenantLicenseData { export interface TenantLicenseData {