Skip to content

Commit 4f2e01a

Browse files
feat!: single endpoint architecture (ADR-026) - Issue #887
BREAKING CHANGE: Renamed agentUrl to endpoint, removed orchestratorUrl and portalUrl - All routes now go through a single Agent endpoint - Dynamic policy path changed from /api/v1/policies/dynamic to /api/v1/dynamic-policies - Added @deprecated annotation on agentUrl() for backwards compatibility - Updated all tests - Migration guide added to CHANGELOG
1 parent fe3d78a commit 4f2e01a

7 files changed

Lines changed: 166 additions & 169 deletions

File tree

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,45 @@ All notable changes to the AxonFlow Java SDK will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.0.0] - 2026-01-05
9+
10+
### Breaking Changes
11+
12+
- **BREAKING**: Renamed `agentUrl` to `endpoint` in `AxonFlowConfig.Builder`
13+
- **BREAKING**: Removed `orchestratorUrl` and `portalUrl` config options (Agent now proxies all routes per ADR-026)
14+
- **BREAKING**: Dynamic policy API path changed from `/api/v1/policies/dynamic` to `/api/v1/dynamic-policies`
15+
16+
### Changed
17+
18+
- All SDK methods now route through single Agent endpoint
19+
- Simplified configuration - only `endpoint()` builder method needed
20+
- Removed `getOrchestratorUrl()` and `getPortalUrl()` config methods (now return endpoint directly)
21+
- Added `@Deprecated` annotation on `agentUrl()` builder method for backwards compatibility
22+
23+
### Migration Guide
24+
25+
**Before (v1.x):**
26+
```java
27+
AxonFlowConfig config = AxonFlowConfig.builder()
28+
.agentUrl("http://localhost:8080")
29+
.orchestratorUrl("http://localhost:8081")
30+
.portalUrl("http://localhost:8082")
31+
.clientId("my-client")
32+
.clientSecret("my-secret")
33+
.build();
34+
```
35+
36+
**After (v2.x):**
37+
```java
38+
AxonFlowConfig config = AxonFlowConfig.builder()
39+
.endpoint("http://localhost:8080")
40+
.clientId("my-client")
41+
.clientSecret("my-secret")
42+
.build();
43+
```
44+
45+
---
46+
847
## [1.13.0] - 2026-01-05
948

1049
### Added

src/main/java/com/getaxonflow/sdk/AxonFlow.java

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private AxonFlow(AxonFlowConfig config) {
127127
this.cache = new ResponseCache(config.getCacheConfig());
128128
this.asyncExecutor = ForkJoinPool.commonPool();
129129

130-
logger.info("AxonFlow client initialized for {}", config.getAgentUrl());
130+
logger.info("AxonFlow client initialized for {}", config.getEndpoint());
131131
}
132132

133133
private static ObjectMapper createObjectMapper() {
@@ -1387,9 +1387,9 @@ public void clearCache() {
13871387
// ========================================================================
13881388

13891389
private Request buildRequest(String method, String path, Object body) {
1390-
HttpUrl url = HttpUrl.parse(config.getAgentUrl() + path);
1390+
HttpUrl url = HttpUrl.parse(config.getEndpoint() + path);
13911391
if (url == null) {
1392-
throw new ConfigurationException("Invalid URL: " + config.getAgentUrl() + path);
1392+
throw new ConfigurationException("Invalid URL: " + config.getEndpoint() + path);
13931393
}
13941394

13951395
Request.Builder builder = new Request.Builder()
@@ -1442,9 +1442,9 @@ private Request buildRequest(String method, String path, Object body) {
14421442
}
14431443

14441444
private Request buildPatchRequest(String path, Object body) {
1445-
HttpUrl url = HttpUrl.parse(config.getAgentUrl() + path);
1445+
HttpUrl url = HttpUrl.parse(config.getEndpoint() + path);
14461446
if (url == null) {
1447-
throw new ConfigurationException("Invalid URL: " + config.getAgentUrl() + path);
1447+
throw new ConfigurationException("Invalid URL: " + config.getEndpoint() + path);
14481448
}
14491449

14501450
Request.Builder builder = new Request.Builder()
@@ -2159,21 +2159,11 @@ public String exportCodeGovernanceDataCSV(ExportOptions options) throws IOExcept
21592159
// ========================================================================
21602160

21612161
/**
2162-
* Gets the orchestrator URL for Execution Replay API.
2163-
* Falls back to agent URL with port 8081 if not configured.
2162+
* Gets the endpoint URL for API requests.
2163+
* All routes now go through a single endpoint (ADR-026 Single Entry Point).
21642164
*/
21652165
private String getOrchestratorUrl() {
2166-
String orchestratorUrl = config.getOrchestratorUrl();
2167-
if (orchestratorUrl != null && !orchestratorUrl.isEmpty()) {
2168-
return orchestratorUrl;
2169-
}
2170-
// Default: assume orchestrator is on same host as agent, port 8081
2171-
try {
2172-
URI uri = URI.create(config.getAgentUrl());
2173-
return uri.getScheme() + "://" + uri.getHost() + ":8081";
2174-
} catch (Exception e) {
2175-
return "http://localhost:8081";
2176-
}
2166+
return config.getEndpoint();
21772167
}
21782168

21792169
/**
@@ -2226,21 +2216,11 @@ private Request buildOrchestratorRequest(String method, String path, Object body
22262216
}
22272217

22282218
/**
2229-
* Gets the portal URL for enterprise PR workflow features.
2230-
* Falls back to agent URL with port 8082 if not configured.
2219+
* Gets the endpoint URL for portal API requests.
2220+
* All routes now go through a single endpoint (ADR-026 Single Entry Point).
22312221
*/
22322222
private String getPortalUrl() {
2233-
String portalUrl = config.getPortalUrl();
2234-
if (portalUrl != null && !portalUrl.isEmpty()) {
2235-
return portalUrl;
2236-
}
2237-
// Default: assume portal is on same host as agent, port 8082
2238-
try {
2239-
URI uri = URI.create(config.getAgentUrl());
2240-
return uri.getScheme() + "://" + uri.getHost() + ":8082";
2241-
} catch (Exception e) {
2242-
return "http://localhost:8082";
2243-
}
2223+
return config.getEndpoint();
22442224
}
22452225

22462226
/**

src/main/java/com/getaxonflow/sdk/AxonFlowConfig.java

Lines changed: 36 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* <p>Use the builder to create a configuration:
3030
* <pre>{@code
3131
* AxonFlowConfig config = AxonFlowConfig.builder()
32-
* .agentUrl("http://localhost:8080")
32+
* .endpoint("http://localhost:8080")
3333
* .clientId("my-client")
3434
* .clientSecret("my-secret")
3535
* .build();
@@ -45,12 +45,10 @@ public final class AxonFlowConfig {
4545
/** Default timeout for HTTP requests. */
4646
public static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(60);
4747

48-
/** Default Agent URL. */
49-
public static final String DEFAULT_AGENT_URL = "http://localhost:8080";
48+
/** Default endpoint URL. */
49+
public static final String DEFAULT_ENDPOINT = "http://localhost:8080";
5050

51-
private final String agentUrl;
52-
private final String orchestratorUrl;
53-
private final String portalUrl;
51+
private final String endpoint;
5452
private final String clientId;
5553
private final String clientSecret;
5654
private final String licenseKey;
@@ -63,9 +61,7 @@ public final class AxonFlowConfig {
6361
private final String userAgent;
6462

6563
private AxonFlowConfig(Builder builder) {
66-
this.agentUrl = normalizeUrl(builder.agentUrl != null ? builder.agentUrl : DEFAULT_AGENT_URL);
67-
this.orchestratorUrl = normalizeUrl(builder.orchestratorUrl);
68-
this.portalUrl = normalizeUrl(builder.portalUrl);
64+
this.endpoint = normalizeUrl(builder.endpoint != null ? builder.endpoint : DEFAULT_ENDPOINT);
6965
this.clientId = builder.clientId;
7066
this.clientSecret = builder.clientSecret;
7167
this.licenseKey = builder.licenseKey;
@@ -81,8 +77,8 @@ private AxonFlowConfig(Builder builder) {
8177
}
8278

8379
private void validate() {
84-
if (agentUrl == null || agentUrl.isEmpty()) {
85-
throw new ConfigurationException("agentUrl is required", "agentUrl");
80+
if (endpoint == null || endpoint.isEmpty()) {
81+
throw new ConfigurationException("endpoint is required", "endpoint");
8682
}
8783
// Credentials are optional for community/self-hosted deployments
8884
// Enterprise features require credentials (validated at method call time)
@@ -106,15 +102,15 @@ private String normalizeUrl(String url) {
106102
}
107103

108104
/**
109-
* Checks if the configured agent URL is localhost.
105+
* Checks if the configured endpoint is localhost.
110106
*
111107
* @return true if connecting to localhost
112108
*/
113109
public boolean isLocalhost() {
114-
return agentUrl != null && (
115-
agentUrl.contains("localhost") ||
116-
agentUrl.contains("127.0.0.1") ||
117-
agentUrl.contains("[::1]")
110+
return endpoint != null && (
111+
endpoint.contains("localhost") ||
112+
endpoint.contains("127.0.0.1") ||
113+
endpoint.contains("[::1]")
118114
);
119115
}
120116

@@ -123,7 +119,7 @@ public boolean isLocalhost() {
123119
*
124120
* <p>Supported environment variables:
125121
* <ul>
126-
* <li>AXONFLOW_AGENT_URL - The Agent URL</li>
122+
* <li>AXONFLOW_AGENT_URL - The endpoint URL (kept for backwards compatibility)</li>
127123
* <li>AXONFLOW_CLIENT_ID - The client ID</li>
128124
* <li>AXONFLOW_CLIENT_SECRET - The client secret</li>
129125
* <li>AXONFLOW_LICENSE_KEY - The license key</li>
@@ -137,9 +133,10 @@ public boolean isLocalhost() {
137133
public static AxonFlowConfig fromEnvironment() {
138134
Builder builder = builder();
139135

140-
String agentUrl = System.getenv("AXONFLOW_AGENT_URL");
141-
if (agentUrl != null && !agentUrl.isEmpty()) {
142-
builder.agentUrl(agentUrl);
136+
// Keep AXONFLOW_AGENT_URL for backwards compatibility, map to endpoint
137+
String endpoint = System.getenv("AXONFLOW_AGENT_URL");
138+
if (endpoint != null && !endpoint.isEmpty()) {
139+
builder.endpoint(endpoint);
143140
}
144141

145142
String clientId = System.getenv("AXONFLOW_CLIENT_ID");
@@ -179,16 +176,8 @@ public static AxonFlowConfig fromEnvironment() {
179176
return builder.build();
180177
}
181178

182-
public String getAgentUrl() {
183-
return agentUrl;
184-
}
185-
186-
public String getOrchestratorUrl() {
187-
return orchestratorUrl;
188-
}
189-
190-
public String getPortalUrl() {
191-
return portalUrl;
179+
public String getEndpoint() {
180+
return endpoint;
192181
}
193182

194183
public String getClientId() {
@@ -242,21 +231,21 @@ public boolean equals(Object o) {
242231
AxonFlowConfig that = (AxonFlowConfig) o;
243232
return debug == that.debug &&
244233
insecureSkipVerify == that.insecureSkipVerify &&
245-
Objects.equals(agentUrl, that.agentUrl) &&
234+
Objects.equals(endpoint, that.endpoint) &&
246235
Objects.equals(clientId, that.clientId) &&
247236
Objects.equals(licenseKey, that.licenseKey) &&
248237
mode == that.mode;
249238
}
250239

251240
@Override
252241
public int hashCode() {
253-
return Objects.hash(agentUrl, clientId, licenseKey, mode, debug, insecureSkipVerify);
242+
return Objects.hash(endpoint, clientId, licenseKey, mode, debug, insecureSkipVerify);
254243
}
255244

256245
@Override
257246
public String toString() {
258247
return "AxonFlowConfig{" +
259-
"agentUrl='" + agentUrl + '\'' +
248+
"endpoint='" + endpoint + '\'' +
260249
", clientId='" + clientId + '\'' +
261250
", mode=" + mode +
262251
", timeout=" + timeout +
@@ -268,9 +257,7 @@ public String toString() {
268257
* Builder for AxonFlowConfig.
269258
*/
270259
public static final class Builder {
271-
private String agentUrl;
272-
private String orchestratorUrl;
273-
private String portalUrl;
260+
private String endpoint;
274261
private String clientId;
275262
private String clientSecret;
276263
private String licenseKey;
@@ -285,41 +272,32 @@ public static final class Builder {
285272
private Builder() {}
286273

287274
/**
288-
* Sets the AxonFlow Agent URL.
275+
* Sets the AxonFlow endpoint URL.
276+
* All routes now go through a single endpoint (ADR-026 Single Entry Point).
289277
*
290-
* @param agentUrl the Agent URL
278+
* @param endpoint the endpoint URL
291279
* @return this builder
292280
*/
293-
public Builder agentUrl(String agentUrl) {
294-
this.agentUrl = agentUrl;
281+
public Builder endpoint(String endpoint) {
282+
this.endpoint = endpoint;
295283
return this;
296284
}
297285

298286
/**
299-
* Sets the Orchestrator URL for Execution Replay API.
300-
*
301-
* <p>If not set, defaults to the Agent URL with port 8081.
287+
* Sets the AxonFlow Agent URL.
288+
* @deprecated Use {@link #endpoint(String)} instead. This method is kept for backwards compatibility.
302289
*
303-
* @param orchestratorUrl the Orchestrator URL
290+
* @param agentUrl the Agent URL
304291
* @return this builder
305292
*/
306-
public Builder orchestratorUrl(String orchestratorUrl) {
307-
this.orchestratorUrl = orchestratorUrl;
293+
@Deprecated
294+
public Builder agentUrl(String agentUrl) {
295+
this.endpoint = agentUrl;
308296
return this;
309297
}
310298

311-
/**
312-
* Sets the Customer Portal URL for enterprise PR workflow features.
313-
*
314-
* <p>If not set, defaults to the Agent URL with port 8082.
315-
*
316-
* @param portalUrl the Customer Portal URL
317-
* @return this builder
318-
*/
319-
public Builder portalUrl(String portalUrl) {
320-
this.portalUrl = portalUrl;
321-
return this;
322-
}
299+
// Note: portalUrl() and orchestratorUrl() methods were removed in v2.0.0
300+
// All routes now go through a single endpoint (ADR-026 Single Entry Point)
323301

324302
/**
325303
* Sets the client ID for authentication.

src/test/java/com/getaxonflow/sdk/AuditReadTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class AuditReadTest {
8585
@BeforeEach
8686
void setUp(WireMockRuntimeInfo wmRuntimeInfo) {
8787
axonflow = AxonFlow.create(AxonFlowConfig.builder()
88-
.agentUrl(wmRuntimeInfo.getHttpBaseUrl())
89-
.orchestratorUrl(wmRuntimeInfo.getHttpBaseUrl())
88+
.endpoint(wmRuntimeInfo.getHttpBaseUrl())
89+
.endpoint(wmRuntimeInfo.getHttpBaseUrl())
9090
.build());
9191
}
9292

0 commit comments

Comments
 (0)