From bc32d7e994ffea4af505fa521932f8b0473b996d Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:58:35 +0200 Subject: [PATCH] fix: use license/usage endpoint for agent/env/app counts Server moved GET /agents to /environments/{envSlug}/agents and removed GET /admin/apps. Replace three broken individual calls with a single GET /admin/license/usage call that returns all counts. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../saas/identity/ServerApiClient.java | 60 +++++++------------ .../saas/portal/TenantPortalService.java | 12 ++-- .../saas/vendor/VendorTenantController.java | 12 ++-- .../saas/portal/TenantPortalServiceTest.java | 3 +- 4 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java index 370fbc6..75d3c90 100644 --- a/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java +++ b/src/main/java/net/siegeln/cameleer/saas/identity/ServerApiClient.java @@ -125,51 +125,37 @@ 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; - } + public record UsageCounts(int agents, int environments, int apps) { + public static final UsageCounts ZERO = new UsageCounts(0, 0, 0); } - /** Fetch environment count from a tenant's server. */ - public int getEnvironmentCount(String serverEndpoint) { + /** Fetch usage counts from a tenant's server via the license usage endpoint. */ + @SuppressWarnings("unchecked") + public UsageCounts getUsageCounts(String serverEndpoint) { try { var resp = RestClient.create().get() - .uri(serverEndpoint + "/api/v1/admin/environments") + .uri(serverEndpoint + "/api/v1/admin/license/usage") .header("Authorization", "Bearer " + getAccessToken()) .header("X-Cameleer-Protocol-Version", "1") .retrieve() - .body(java.util.List.class); - return resp != null ? resp.size() : 0; + .body(Map.class); + if (resp == null) return UsageCounts.ZERO; + var limits = (List>) resp.get("limits"); + if (limits == null) return UsageCounts.ZERO; + int agents = 0, environments = 0, apps = 0; + for (var row : limits) { + String key = (String) row.get("key"); + int current = ((Number) row.get("current")).intValue(); + switch (key) { + case "max_agents" -> agents = current; + case "max_environments" -> environments = current; + case "max_apps" -> apps = current; + } + } + return new UsageCounts(agents, environments, apps); } catch (Exception e) { - log.warn("Environment count fetch failed for {}: {}", serverEndpoint, e.getMessage()); - return 0; - } - } - - /** Fetch app count from a tenant's server. */ - public int getAppCount(String serverEndpoint) { - try { - var resp = RestClient.create().get() - .uri(serverEndpoint + "/api/v1/admin/apps") - .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("App count fetch failed for {}: {}", serverEndpoint, e.getMessage()); - return 0; + log.warn("Usage counts fetch failed for {}: {}", serverEndpoint, e.getMessage()); + return UsageCounts.ZERO; } } diff --git a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java index 29cff96..63a3021 100644 --- a/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java +++ b/src/main/java/net/siegeln/cameleer/saas/portal/TenantPortalService.java @@ -113,8 +113,9 @@ public class TenantPortalService { serverHealthy = health.healthy(); serverStatus = health.status(); if (serverHealthy) { - agentCount = serverApiClient.getAgentCount(endpoint); - environmentCount = serverApiClient.getEnvironmentCount(endpoint); + var counts = serverApiClient.getUsageCounts(endpoint); + agentCount = counts.agents(); + environmentCount = counts.environments(); } } @@ -160,9 +161,10 @@ public class TenantPortalService { Map usage = new HashMap<>(); String endpoint = tenant.getServerEndpoint(); if (endpoint != null && !endpoint.isBlank()) { - usage.put("agents", serverApiClient.getAgentCount(endpoint)); - usage.put("environments", serverApiClient.getEnvironmentCount(endpoint)); - usage.put("apps", serverApiClient.getAppCount(endpoint)); + var counts = serverApiClient.getUsageCounts(endpoint); + usage.put("agents", counts.agents()); + usage.put("environments", counts.environments()); + usage.put("apps", counts.apps()); } // User count from Logto org membership String orgId = tenant.getLogtoOrgId(); diff --git a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantController.java b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantController.java index 46d7e12..a1bc066 100644 --- a/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantController.java +++ b/src/main/java/net/siegeln/cameleer/saas/vendor/VendorTenantController.java @@ -80,8 +80,9 @@ public class VendorTenantController { boolean isActive = "ACTIVE".equals(tenant.getStatus().name()); if (isActive && endpoint != null && !endpoint.isBlank() && "RUNNING".equals(status.state().name())) { var serverApi = vendorTenantService.getServerApiClient(); - agentCount = serverApi.getAgentCount(endpoint); - environmentCount = serverApi.getEnvironmentCount(endpoint); + var counts = serverApi.getUsageCounts(endpoint); + agentCount = counts.agents(); + environmentCount = counts.environments(); } var license = vendorTenantService.getLicenseForTenant(tenant.getId()); if (license.isPresent() && license.get().getLimits() != null) { @@ -130,9 +131,10 @@ public class VendorTenantController { String endpoint = tenant.getServerEndpoint(); if (health.healthy() && endpoint != null && !endpoint.isBlank()) { var serverApi = vendorTenantService.getServerApiClient(); - usage.put("agents", serverApi.getAgentCount(endpoint)); - usage.put("environments", serverApi.getEnvironmentCount(endpoint)); - usage.put("apps", serverApi.getAppCount(endpoint)); + var counts = serverApi.getUsageCounts(endpoint); + usage.put("agents", counts.agents()); + usage.put("environments", counts.environments()); + usage.put("apps", counts.apps()); } return ResponseEntity.ok(new VendorTenantDetail( diff --git a/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java b/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java index 17434e3..07ce49a 100644 --- a/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java +++ b/src/test/java/net/siegeln/cameleer/saas/portal/TenantPortalServiceTest.java @@ -108,8 +108,7 @@ class TenantPortalServiceTest { when(tenantService.getById(tenantId)).thenReturn(Optional.of(tenant)); 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(serverApiClient.getUsageCounts("http://server:8080")).thenReturn(new ServerApiClient.UsageCounts(3, 1, 2)); when(licenseService.getActiveLicense(tenantId)).thenReturn(Optional.of(license)); var result = tenantPortalService.getDashboard();