refactor(license): extract cameleer-license-api module from server-core
Some checks failed
CI / cleanup-branch (push) Has been skipped
CI / build (push) Failing after 2m57s
CI / docker (push) Has been skipped
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Has been skipped

Splits the pure license contract types (LicenseInfo, LicenseValidator,
LicenseState, LicenseStateMachine, LicenseLimits, DefaultTierLimits) into a
new cameleer-license-api module under package com.cameleer.license.

Why: cameleer-license-minter previously depended on cameleer-server-core for
these types, dragging cameleer-server-core + cameleer-common onto the
classpath of every minter consumer (notably cameleer-saas). The SaaS
management plane has no business carrying server-runtime types — it only
needs the license contract to mint and verify tokens.

After:
  cameleer-license-minter -> cameleer-license-api  (no server internals)
  cameleer-server-core    -> cameleer-license-api
  cameleer-saas           -> cameleer-license-minter -> cameleer-license-api

Verified: mvn -pl cameleer-license-minter dependency:tree shows the minter
no longer pulls cameleer-server-core or cameleer-common. Full reactor
verify (-DskipITs) green: 371 tests pass.

LicenseGate stays in server-core (server-runtime state holder, not contract).

Closes cameleer/cameleer-server#156

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-26 20:06:52 +02:00
parent 30db609aff
commit 858975f03f
43 changed files with 130 additions and 52 deletions

View File

@@ -47,14 +47,19 @@ paths:
## license/ — License domain (signed-token tier system)
The pure license **contract types** live in the separate `cameleer-license-api` module under package `com.cameleer.license` (no Spring, no server-runtime deps) so consumers like `cameleer-license-minter` and `cameleer-saas` can use them without inheriting server internals. Server-core only contains the runtime state holder (`LicenseGate`).
Contract types in `cameleer-license-api` (package `com.cameleer.license`):
- `LicenseInfo` — record: `(UUID licenseId, String tenantId, String label, Map<String,Integer> limits, Instant issuedAt, Instant expiresAt, int gracePeriodDays)`. `isExpired()` true once `now > expiresAt + gracePeriodDays`; `isAfterRawExpiry()` true once `now > expiresAt`. Constructed via `LicenseValidator`; canonical ctor null-checks all required fields and rejects blank tenantId / negative grace.
- `LicenseLimits` — typed limits container backed by `Map<String,Integer>`. `defaultsOnly()` returns the `DefaultTierLimits.DEFAULTS` view; `mergeOverDefaults(overrides)` produces the license-overrides UNION default tier. `get(String key)` returns the cap; throws `IllegalArgumentException` for unknown keys (programmer error). `isDefaultSourced(key, license)` reports whether a key fell through to the default tier.
- `DefaultTierLimits` — immutable `LinkedHashMap` of constants for the no-license fallback tier: `max_environments=1, max_apps=3, max_agents=5, max_users=3, max_outbound_connections=1, max_alert_rules=2, max_total_cpu_millis=2000, max_total_memory_mb=2048, max_total_replicas=5, max_execution_retention_days=1, max_log_retention_days=1, max_metric_retention_days=1, max_jar_retention_count=3`.
- `LicenseValidator` — verifies signed token. Constructor `(String publicKeyBase64, String expectedTenantId)` decodes an X.509 Ed25519 public key. `validate(String token)` splits `payload.signature`, verifies the Ed25519 signature, parses the JSON payload, enforces `tenantId == expectedTenantId`, and returns `LicenseInfo`. Throws `SecurityException` on signature mismatch / `IllegalArgumentException` on parse failure / expired payload.
- `LicenseGate` — runtime state holder (thread-safe via `AtomicReference<Snapshot>`). `getCurrent()` returns the current `LicenseInfo` (null when ABSENT/INVALID); `getState()` delegates to `LicenseStateMachine.classify(...)`; `getEffectiveLimits()` returns license-overrides UNION defaults in `ACTIVE`/`GRACE`, defaults-only otherwise. `getInvalidReason()`, `load(LicenseInfo)`, `markInvalid(String reason)`, `clear()` are the mutators. `getLimit(key, defaultValue)` shorthand swallows unknown-key errors.
- `LicenseStateMachine` — pure classifier. `classify(LicenseInfo, String invalidReason)` returns `INVALID` if a reason is set, `ABSENT` if no license, `ACTIVE` if `now <= expiresAt`, `GRACE` if expired but within grace window, `EXPIRED` otherwise.
- `LicenseState` — enum: `ABSENT, ACTIVE, GRACE, EXPIRED, INVALID`.
Runtime state holder in server-core (package `com.cameleer.server.core.license`):
- `LicenseGate` — runtime state holder (thread-safe via `AtomicReference<Snapshot>`). `getCurrent()` returns the current `LicenseInfo` (null when ABSENT/INVALID); `getState()` delegates to `LicenseStateMachine.classify(...)`; `getEffectiveLimits()` returns license-overrides UNION defaults in `ACTIVE`/`GRACE`, defaults-only otherwise. `getInvalidReason()`, `load(LicenseInfo)`, `markInvalid(String reason)`, `clear()` are the mutators. `getLimit(key, defaultValue)` shorthand swallows unknown-key errors.
## search/ — Execution search and stats
- `SearchService` — search, count, stats, statsForApp, statsForRoute, timeseries, timeseriesForApp, timeseriesForRoute, timeseriesGroupedByApp, timeseriesGroupedByRoute, slaCompliance, slaCountsByApp, slaCountsByRoute, topErrors, activeErrorTypes, punchcard, distinctAttributeKeys. `statsForRoute`/`timeseriesForRoute` take `(routeId, applicationId)` — app filter is applied to `stats_1m_route`.

View File

@@ -14,8 +14,10 @@ Cameleer Server — observability server that receives, stores, and serves Camel
## Modules
- `cameleer-license-api` — pure license contract types (`LicenseInfo`, `LicenseValidator`, `LicenseState`, `LicenseStateMachine`, `LicenseLimits`, `DefaultTierLimits`) under package `com.cameleer.license`. No Spring or server-runtime deps; consumed by `cameleer-server-core` (validation/runtime gate) and `cameleer-license-minter` (vendor signing) — and transitively by `cameleer-saas` via the minter — without inheriting server internals.
- `cameleer-server-core` — domain logic, storage interfaces, services (no Spring dependencies)
- `cameleer-server-app` — Spring Boot web app, REST controllers, SSE, persistence, Docker orchestration
- `cameleer-license-minter` — vendor-only Ed25519 license signing library + CLI. Depends only on `cameleer-license-api` so consumers don't pull in `cameleer-server-core`.
## Build Commands

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-server-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cameleer-license-api</artifactId>
<name>Cameleer License API</name>
<description>Pure license contract types — LicenseInfo, LicenseValidator, LicenseState, LicenseStateMachine, LicenseLimits, DefaultTierLimits. Shared by server-core (validation/runtime gate) and cameleer-license-minter (vendor-side signing). Has no Spring or server-runtime dependencies so consumers like cameleer-saas can depend on the minter without inheriting server internals.</description>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<!-- Plain library JAR — no repackage. -->
<execution>
<id>repackage</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import java.util.Collections;
import java.util.LinkedHashMap;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import java.time.Instant;
import java.util.Map;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import java.util.Collections;
import java.util.LinkedHashMap;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
public enum LicenseState {
ABSENT,

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
public final class LicenseStateMachine {

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import org.junit.jupiter.api.Test;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import org.junit.jupiter.api.Test;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import org.junit.jupiter.api.Test;

View File

@@ -1,4 +1,4 @@
package com.cameleer.server.core.license;
package com.cameleer.license;
import org.junit.jupiter.api.Test;

View File

@@ -58,7 +58,7 @@ Two JARs land in `cameleer-license-minter/target/`:
```java
import com.cameleer.license.minter.LicenseMinter;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
LicenseInfo info = new LicenseInfo(
java.util.UUID.randomUUID(),
@@ -136,11 +136,11 @@ base64(canonicalJson) + "." + base64(ed25519Signature)
- The signature is computed with `Signature.getInstance("Ed25519")` over the canonical payload bytes (not over the base64-encoded form).
- Encoding is `Base64.getEncoder()` (RFC 4648 §4 — *not* base64url). The validator decodes with the matching `Base64.getDecoder()`.
`LicenseValidator.validate(...)` (`cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseValidator.java:42`) splits on the first `.`, decodes both halves, verifies the signature, then deserializes the payload.
`LicenseValidator.validate(...)` (`cameleer-license-api/src/main/java/com/cameleer/license/LicenseValidator.java:42`) splits on the first `.`, decodes both halves, verifies the signature, then deserializes the payload.
## LicenseInfo schema
Source: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/LicenseInfo.java`. Field-by-field:
Source: `cameleer-license-api/src/main/java/com/cameleer/license/LicenseInfo.java`. Field-by-field:
| Field | Type | Required | Semantics |
|---|---|---|---|
@@ -154,7 +154,7 @@ Source: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/Lic
## Limits dictionary
Canonical key set: `cameleer-server-core/src/main/java/com/cameleer/server/core/license/DefaultTierLimits.java`. Any key not listed here is silently ignored by the server's `LicenseGate.getEffectiveLimits()`.
Canonical key set: `cameleer-license-api/src/main/java/com/cameleer/license/DefaultTierLimits.java`. Any key not listed here is silently ignored by the server's `LicenseGate.getEffectiveLimits()`.
| CLI flag | Key | Default | What the server enforces |
|---|---|---|---|
@@ -284,4 +284,4 @@ mvn -pl cameleer-server-app dependency:tree | grep license-minter
# expected: empty output (or, in development branches, a single line scoped 'test')
```
`cameleer-license-minter/pom.xml` depends on `cameleer-server-core` for `LicenseInfo` and the validator round-trip used by `--verify`. The server app intentionally does not depend on the minter — vendors mint outside the customer-deployed runtime, and a compromised customer cannot leverage server code to forge tokens.
`cameleer-license-minter/pom.xml` depends on `cameleer-license-api` for the pure license contract types (`LicenseInfo`, `LicenseValidator`) used by mint + `--verify`. It deliberately does **not** depend on `cameleer-server-core`, so consumers of the minter (e.g. `cameleer-saas`) do not inherit server-runtime types onto their classpath. The server app intentionally does not depend on the minter — vendors mint outside the customer-deployed runtime, and a compromised customer cannot leverage server code to forge tokens.

View File

@@ -17,7 +17,7 @@
<dependencies>
<dependency>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-server-core</artifactId>
<artifactId>cameleer-license-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>

View File

@@ -1,6 +1,6 @@
package com.cameleer.license.minter;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ObjectNode;

View File

@@ -1,7 +1,7 @@
package com.cameleer.license.minter.cli;
import com.cameleer.license.minter.LicenseMinter;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import java.io.PrintStream;
import java.nio.file.Files;
@@ -107,7 +107,7 @@ public final class LicenseMinterCli {
}
try {
String pubB64 = Files.readString(Path.of(pubPath)).trim();
new com.cameleer.server.core.license.LicenseValidator(pubB64, tenant).validate(token);
new com.cameleer.license.LicenseValidator(pubB64, tenant).validate(token);
out.println("verified ok");
} catch (Exception ve) {
err.println("VERIFY FAILED: " + ve.getMessage());

View File

@@ -1,7 +1,7 @@
package com.cameleer.license.minter;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseValidator;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseValidator;
import org.junit.jupiter.api.Test;
import java.security.KeyPair;

View File

@@ -1,6 +1,6 @@
package com.cameleer.license.minter.cli;
import com.cameleer.server.core.license.LicenseValidator;
import com.cameleer.license.LicenseValidator;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

View File

@@ -4,8 +4,8 @@ import com.cameleer.server.app.license.LicenseRepository;
import com.cameleer.server.app.license.LicenseService;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseValidator;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseValidator;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -3,7 +3,7 @@ package com.cameleer.server.app.controller;
import com.cameleer.server.app.license.LicenseRepository;
import com.cameleer.server.app.license.LicenseService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import java.util.Objects;

View File

@@ -3,8 +3,8 @@ package com.cameleer.server.app.license;
import com.cameleer.server.core.admin.AuditCategory;
import com.cameleer.server.core.admin.AuditResult;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.license.LicenseLimits;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseLimits;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import java.time.Duration;
import java.time.Instant;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseState;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Value;

View File

@@ -3,9 +3,9 @@ package com.cameleer.server.app.license;
import com.cameleer.server.core.admin.AuditCategory;
import com.cameleer.server.core.admin.AuditResult;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseValidator;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseLimits;
import com.cameleer.license.LicenseLimits;
import com.cameleer.server.core.runtime.Environment;
import com.cameleer.server.core.runtime.EnvironmentRepository;
import org.slf4j.Logger;

View File

@@ -2,7 +2,7 @@ package com.cameleer.server.app;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import com.cameleer.server.core.security.JwtService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;

View File

@@ -22,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Verifies that the {@code max_alert_rules} cap from the default tier is enforced at
* {@code POST /api/v1/environments/{envSlug}/alerts/rules}. Default tier
* {@code max_alert_rules = 2}; with no license installed the gate is in
* {@link com.cameleer.server.core.license.LicenseState#ABSENT} and the defaults are
* {@link com.cameleer.license.LicenseState#ABSENT} and the defaults are
* authoritative. The first two creates succeed; the third must be rejected with the
* structured 403 envelope produced by {@link LicenseExceptionAdvice}.
*/

View File

@@ -19,7 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that the {@code max_apps} cap from the default tier is enforced at
* {@code POST /api/v1/environments/{envSlug}/apps}. Default tier {@code max_apps = 3}; with no
* license installed the gate is in {@link com.cameleer.server.core.license.LicenseState#ABSENT}
* license installed the gate is in {@link com.cameleer.license.LicenseState#ABSENT}
* and the defaults are authoritative. The fourth create attempt must be rejected with the
* structured 403 envelope produced by {@link LicenseExceptionAdvice}.
*/

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import org.junit.jupiter.api.Test;
import java.time.Instant;

View File

@@ -4,8 +4,8 @@ import com.cameleer.license.minter.LicenseMinter;
import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import org.junit.jupiter.api.Test;
import java.time.Instant;

View File

@@ -1,7 +1,7 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.license.LicenseInfo;
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import org.junit.jupiter.api.Test;

View File

@@ -4,9 +4,9 @@ import com.cameleer.server.core.admin.AuditCategory;
import com.cameleer.server.core.admin.AuditResult;
import com.cameleer.server.core.admin.AuditService;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.server.core.license.LicenseValidator;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import com.cameleer.license.LicenseValidator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationEventPublisher;

View File

@@ -20,7 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Verifies that the {@code max_outbound_connections} cap from the default tier is enforced at
* {@code POST /api/v1/admin/outbound-connections}. Default tier
* {@code max_outbound_connections = 1}; with no license installed the gate is in
* {@link com.cameleer.server.core.license.LicenseState#ABSENT} and the defaults are
* {@link com.cameleer.license.LicenseState#ABSENT} and the defaults are
* authoritative. The first create succeeds; the second must be rejected with the structured
* 403 envelope produced by {@link LicenseExceptionAdvice}.
*/

View File

@@ -1,8 +1,8 @@
package com.cameleer.server.app.license;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseInfo;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import com.cameleer.server.core.runtime.Environment;
import com.cameleer.server.core.runtime.EnvironmentRepository;
import org.junit.jupiter.api.BeforeEach;

View File

@@ -3,7 +3,7 @@ package com.cameleer.server.app.license;
import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import com.cameleer.server.core.license.LicenseGate;
import com.cameleer.server.core.license.LicenseState;
import com.cameleer.license.LicenseState;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

View File

@@ -15,6 +15,10 @@
<description>Domain logic, storage, and agent registry</description>
<dependencies>
<dependency>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-license-api</artifactId>
</dependency>
<dependency>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-common</artifactId>

View File

@@ -1,5 +1,9 @@
package com.cameleer.server.core.license;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseLimits;
import com.cameleer.license.LicenseState;
import com.cameleer.license.LicenseStateMachine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,5 +1,8 @@
package com.cameleer.server.core.license;
import com.cameleer.license.DefaultTierLimits;
import com.cameleer.license.LicenseInfo;
import com.cameleer.license.LicenseState;
import org.junit.jupiter.api.Test;
import java.time.Instant;

View File

@@ -20,6 +20,7 @@
<description>Observability server for Cameleer agents</description>
<modules>
<module>cameleer-license-api</module>
<module>cameleer-server-core</module>
<module>cameleer-server-app</module>
<module>cameleer-license-minter</module>
@@ -40,6 +41,11 @@
<artifactId>cameleer-common</artifactId>
<version>${cameleer-common.version}</version>
</dependency>
<dependency>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-license-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.cameleer</groupId>
<artifactId>cameleer-server-core</artifactId>