refactor: rename tiers and rewrite LicenseDefaults to 13-key cap matrix
- Tier enum: LOW→STARTER, MID→TEAM, HIGH→BUSINESS, BUSINESS→ENTERPRISE - LicenseDefaults: 13-key limits per tier matching server handoff cap matrix - Drop features concept from LicenseEntity, LicenseResponse, portal DTOs - Add label and gracePeriodDays to LicenseEntity - Fix agent limit key from 'agents' to 'max_agents' in VendorTenantController Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,15 +55,6 @@ public class LicenseController {
|
||||
}
|
||||
|
||||
private LicenseResponse toResponse(LicenseEntity entity) {
|
||||
return new LicenseResponse(
|
||||
entity.getId(),
|
||||
entity.getTenantId(),
|
||||
entity.getTier(),
|
||||
entity.getFeatures(),
|
||||
entity.getLimits(),
|
||||
entity.getIssuedAt(),
|
||||
entity.getExpiresAt(),
|
||||
entity.getToken()
|
||||
);
|
||||
return LicenseResponse.from(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,37 +8,71 @@ public final class LicenseDefaults {
|
||||
|
||||
private LicenseDefaults() {}
|
||||
|
||||
public static Map<String, Object> featuresForTier(Tier tier) {
|
||||
return switch (tier) {
|
||||
case LOW -> Map.of(
|
||||
"topology", true, "lineage", false,
|
||||
"correlation", false, "debugger", false, "replay", false);
|
||||
case MID -> Map.of(
|
||||
"topology", true, "lineage", true,
|
||||
"correlation", true, "debugger", false, "replay", false);
|
||||
case HIGH -> Map.of(
|
||||
"topology", true, "lineage", true,
|
||||
"correlation", true, "debugger", true, "replay", true);
|
||||
case BUSINESS -> Map.of(
|
||||
"topology", true, "lineage", true,
|
||||
"correlation", true, "debugger", true, "replay", true);
|
||||
};
|
||||
}
|
||||
public static final int DEFAULT_GRACE_PERIOD_DAYS = 14;
|
||||
public static final int DEFAULT_LICENSE_DAYS = 365;
|
||||
|
||||
public static Map<String, Object> limitsForTier(Tier tier) {
|
||||
public static Map<String, Integer> limitsForTier(Tier tier) {
|
||||
return switch (tier) {
|
||||
case LOW -> Map.of(
|
||||
"max_agents", 3, "retention_days", 7,
|
||||
"max_environments", 1);
|
||||
case MID -> Map.of(
|
||||
"max_agents", 10, "retention_days", 30,
|
||||
"max_environments", 2);
|
||||
case HIGH -> Map.of(
|
||||
"max_agents", 50, "retention_days", 90,
|
||||
"max_environments", -1);
|
||||
case BUSINESS -> Map.of(
|
||||
"max_agents", -1, "retention_days", 365,
|
||||
"max_environments", -1);
|
||||
case STARTER -> Map.ofEntries(
|
||||
Map.entry("max_environments", 2),
|
||||
Map.entry("max_apps", 10),
|
||||
Map.entry("max_agents", 20),
|
||||
Map.entry("max_users", 5),
|
||||
Map.entry("max_outbound_connections", 5),
|
||||
Map.entry("max_alert_rules", 10),
|
||||
Map.entry("max_total_cpu_millis", 8000),
|
||||
Map.entry("max_total_memory_mb", 8192),
|
||||
Map.entry("max_total_replicas", 25),
|
||||
Map.entry("max_execution_retention_days", 7),
|
||||
Map.entry("max_log_retention_days", 7),
|
||||
Map.entry("max_metric_retention_days", 7),
|
||||
Map.entry("max_jar_retention_count", 5)
|
||||
);
|
||||
case TEAM -> Map.ofEntries(
|
||||
Map.entry("max_environments", 5),
|
||||
Map.entry("max_apps", 50),
|
||||
Map.entry("max_agents", 100),
|
||||
Map.entry("max_users", 25),
|
||||
Map.entry("max_outbound_connections", 25),
|
||||
Map.entry("max_alert_rules", 50),
|
||||
Map.entry("max_total_cpu_millis", 32000),
|
||||
Map.entry("max_total_memory_mb", 32768),
|
||||
Map.entry("max_total_replicas", 100),
|
||||
Map.entry("max_execution_retention_days", 30),
|
||||
Map.entry("max_log_retention_days", 30),
|
||||
Map.entry("max_metric_retention_days", 30),
|
||||
Map.entry("max_jar_retention_count", 10)
|
||||
);
|
||||
case BUSINESS -> Map.ofEntries(
|
||||
Map.entry("max_environments", 10),
|
||||
Map.entry("max_apps", 200),
|
||||
Map.entry("max_agents", 500),
|
||||
Map.entry("max_users", 100),
|
||||
Map.entry("max_outbound_connections", 100),
|
||||
Map.entry("max_alert_rules", 200),
|
||||
Map.entry("max_total_cpu_millis", 128000),
|
||||
Map.entry("max_total_memory_mb", 131072),
|
||||
Map.entry("max_total_replicas", 500),
|
||||
Map.entry("max_execution_retention_days", 90),
|
||||
Map.entry("max_log_retention_days", 90),
|
||||
Map.entry("max_metric_retention_days", 90),
|
||||
Map.entry("max_jar_retention_count", 25)
|
||||
);
|
||||
case ENTERPRISE -> Map.ofEntries(
|
||||
Map.entry("max_environments", 50),
|
||||
Map.entry("max_apps", 1000),
|
||||
Map.entry("max_agents", 5000),
|
||||
Map.entry("max_users", 1000),
|
||||
Map.entry("max_outbound_connections", 500),
|
||||
Map.entry("max_alert_rules", 1000),
|
||||
Map.entry("max_total_cpu_millis", 512000),
|
||||
Map.entry("max_total_memory_mb", 524288),
|
||||
Map.entry("max_total_replicas", 2000),
|
||||
Map.entry("max_execution_retention_days", 365),
|
||||
Map.entry("max_log_retention_days", 180),
|
||||
Map.entry("max_metric_retention_days", 180),
|
||||
Map.entry("max_jar_retention_count", 50)
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,16 @@ public class LicenseEntity {
|
||||
@Column(name = "tier", nullable = false, length = 20)
|
||||
private String tier;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "features", nullable = false, columnDefinition = "jsonb")
|
||||
private Map<String, Object> features;
|
||||
@Column(name = "label")
|
||||
private String label;
|
||||
|
||||
@JdbcTypeCode(SqlTypes.JSON)
|
||||
@Column(name = "limits", nullable = false, columnDefinition = "jsonb")
|
||||
private Map<String, Object> limits;
|
||||
|
||||
@Column(name = "grace_period_days", nullable = false)
|
||||
private int gracePeriodDays;
|
||||
|
||||
@Column(name = "issued_at", nullable = false)
|
||||
private Instant issuedAt;
|
||||
|
||||
@@ -62,10 +64,12 @@ public class LicenseEntity {
|
||||
public void setTenantId(UUID tenantId) { this.tenantId = tenantId; }
|
||||
public String getTier() { return tier; }
|
||||
public void setTier(String tier) { this.tier = tier; }
|
||||
public Map<String, Object> getFeatures() { return features; }
|
||||
public void setFeatures(Map<String, Object> features) { this.features = features; }
|
||||
public String getLabel() { return label; }
|
||||
public void setLabel(String label) { this.label = label; }
|
||||
public Map<String, Object> getLimits() { return limits; }
|
||||
public void setLimits(Map<String, Object> limits) { this.limits = limits; }
|
||||
public int getGracePeriodDays() { return gracePeriodDays; }
|
||||
public void setGracePeriodDays(int gracePeriodDays) { this.gracePeriodDays = gracePeriodDays; }
|
||||
public Instant getIssuedAt() { return issuedAt; }
|
||||
public void setIssuedAt(Instant issuedAt) { this.issuedAt = issuedAt; }
|
||||
public Instant getExpiresAt() { return expiresAt; }
|
||||
|
||||
@@ -23,7 +23,6 @@ public class LicenseService {
|
||||
}
|
||||
|
||||
public LicenseEntity generateLicense(TenantEntity tenant, Duration validity, UUID actorId) {
|
||||
var features = LicenseDefaults.featuresForTier(tenant.getTier());
|
||||
var limits = LicenseDefaults.limitsForTier(tenant.getTier());
|
||||
Instant now = Instant.now();
|
||||
Instant expiresAt = now.plus(validity);
|
||||
@@ -33,10 +32,10 @@ public class LicenseService {
|
||||
var entity = new LicenseEntity();
|
||||
entity.setTenantId(tenant.getId());
|
||||
entity.setTier(tenant.getTier().name());
|
||||
entity.setFeatures(features);
|
||||
entity.setLimits(limits);
|
||||
entity.setLimits(new java.util.HashMap<>(limits));
|
||||
entity.setIssuedAt(now);
|
||||
entity.setExpiresAt(expiresAt);
|
||||
entity.setGracePeriodDays(LicenseDefaults.DEFAULT_GRACE_PERIOD_DAYS);
|
||||
entity.setToken(token);
|
||||
|
||||
var saved = licenseRepository.save(entity);
|
||||
@@ -75,7 +74,6 @@ public class LicenseService {
|
||||
.map(e -> Map.<String, Object>of(
|
||||
"tenant_id", e.getTenantId().toString(),
|
||||
"tier", e.getTier(),
|
||||
"features", e.getFeatures(),
|
||||
"limits", e.getLimits()
|
||||
));
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ public record LicenseResponse(
|
||||
UUID id,
|
||||
UUID tenantId,
|
||||
String tier,
|
||||
Map<String, Object> features,
|
||||
String label,
|
||||
Map<String, Object> limits,
|
||||
int gracePeriodDays,
|
||||
Instant issuedAt,
|
||||
Instant expiresAt,
|
||||
String token
|
||||
@@ -19,7 +20,7 @@ public record LicenseResponse(
|
||||
public static LicenseResponse from(LicenseEntity e) {
|
||||
return new LicenseResponse(
|
||||
e.getId(), e.getTenantId(), e.getTier(),
|
||||
e.getFeatures(), e.getLimits(),
|
||||
e.getLabel(), e.getLimits(), e.getGracePeriodDays(),
|
||||
e.getIssuedAt(), e.getExpiresAt(),
|
||||
e.getToken()
|
||||
);
|
||||
|
||||
@@ -61,13 +61,14 @@ public class TenantPortalService {
|
||||
String name, String slug, String tier, String status,
|
||||
boolean serverHealthy, String serverStatus, String serverEndpoint,
|
||||
String licenseTier, long licenseDaysRemaining,
|
||||
Map<String, Object> limits, Map<String, Object> features,
|
||||
Map<String, Object> limits,
|
||||
int agentCount, int environmentCount
|
||||
) {}
|
||||
|
||||
public record LicenseData(
|
||||
UUID id, String tier, Map<String, Object> features, Map<String, Object> limits,
|
||||
Instant issuedAt, Instant expiresAt, String token, long daysRemaining
|
||||
UUID id, String tier, String label, Map<String, Object> limits,
|
||||
int gracePeriodDays, Instant issuedAt, Instant expiresAt,
|
||||
String token, long daysRemaining
|
||||
) {}
|
||||
|
||||
public record TenantSettingsData(
|
||||
@@ -118,7 +119,6 @@ public class TenantPortalService {
|
||||
String licenseTier = null;
|
||||
long licenseDaysRemaining = 0;
|
||||
Map<String, Object> limits = Map.of();
|
||||
Map<String, Object> features = Map.of();
|
||||
|
||||
var licenseOpt = licenseService.getActiveLicense(tenant.getId());
|
||||
if (licenseOpt.isPresent()) {
|
||||
@@ -126,7 +126,6 @@ public class TenantPortalService {
|
||||
licenseTier = lic.getTier();
|
||||
licenseDaysRemaining = daysUntil(lic.getExpiresAt());
|
||||
limits = lic.getLimits() != null ? lic.getLimits() : Map.of();
|
||||
features = lic.getFeatures() != null ? lic.getFeatures() : Map.of();
|
||||
}
|
||||
|
||||
return new DashboardData(
|
||||
@@ -134,7 +133,7 @@ public class TenantPortalService {
|
||||
tenant.getTier().name(), tenant.getStatus().name(),
|
||||
serverHealthy, serverStatus, endpoint,
|
||||
licenseTier, licenseDaysRemaining,
|
||||
limits, features, agentCount, environmentCount
|
||||
limits, agentCount, environmentCount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,9 +141,9 @@ public class TenantPortalService {
|
||||
TenantEntity tenant = resolveTenant();
|
||||
return licenseService.getActiveLicense(tenant.getId())
|
||||
.map(lic -> new LicenseData(
|
||||
lic.getId(), lic.getTier(),
|
||||
lic.getFeatures() != null ? lic.getFeatures() : Map.of(),
|
||||
lic.getId(), lic.getTier(), lic.getLabel(),
|
||||
lic.getLimits() != null ? lic.getLimits() : Map.of(),
|
||||
lic.getGracePeriodDays(),
|
||||
lic.getIssuedAt(), lic.getExpiresAt(),
|
||||
lic.getToken(), daysUntil(lic.getExpiresAt())
|
||||
))
|
||||
|
||||
@@ -33,7 +33,7 @@ public class TenantEntity {
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "tier", nullable = false, length = 20)
|
||||
private Tier tier = Tier.LOW;
|
||||
private Tier tier = Tier.STARTER;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", nullable = false, length = 20)
|
||||
|
||||
@@ -31,7 +31,7 @@ public class TenantService {
|
||||
var entity = new TenantEntity();
|
||||
entity.setName(request.name());
|
||||
entity.setSlug(request.slug());
|
||||
entity.setTier(request.tier() != null ? Tier.valueOf(request.tier()) : Tier.LOW);
|
||||
entity.setTier(request.tier() != null ? Tier.valueOf(request.tier()) : Tier.STARTER);
|
||||
entity.setStatus(TenantStatus.PROVISIONING);
|
||||
|
||||
var saved = tenantRepository.save(entity);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package net.siegeln.cameleer.saas.tenant;
|
||||
|
||||
public enum Tier {
|
||||
LOW, MID, HIGH, BUSINESS
|
||||
STARTER, TEAM, BUSINESS, ENTERPRISE
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ public class VendorTenantController {
|
||||
var license = vendorTenantService.getLicenseForTenant(tenant.getId());
|
||||
if (license.isPresent() && license.get().getLimits() != null) {
|
||||
var limits = license.get().getLimits();
|
||||
if (limits.containsKey("agents")) {
|
||||
agentLimit = ((Number) limits.get("agents")).intValue();
|
||||
if (limits.containsKey("max_agents")) {
|
||||
agentLimit = ((Number) limits.get("max_agents")).intValue();
|
||||
}
|
||||
}
|
||||
return new VendorTenantSummary(
|
||||
|
||||
Reference in New Issue
Block a user