Skip to content

Commit b48ecaa

Browse files
feat: add Code Governance Git Provider APIs (#15)
Enterprise features for creating PRs from LLM-generated code: - validateGitProvider() - Validate credentials before saving - configureGitProvider() - Configure GitHub, GitLab, or Bitbucket - listGitProviders() - List configured providers - deleteGitProvider() - Remove a provider - createPR() - Create PR from generated code with audit trail - listPRs() - List PRs with filtering - getPR() - Get PR details - syncPRStatus() - Sync status from Git provider Supported Git Providers: - GitHub (Cloud and Enterprise Server) - GitLab (Cloud and Self-Managed) - Bitbucket (Cloud and Server/Data Center) Bump version to 1.4.0
1 parent 43604f4 commit b48ecaa

18 files changed

Lines changed: 1650 additions & 1 deletion

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ 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+
## [1.4.0] - 2025-12-29
9+
10+
### Added
11+
12+
- **Code Governance Git Provider APIs** (Enterprise): Create PRs from LLM-generated code
13+
- `validateGitProvider()` - Validate credentials before saving
14+
- `configureGitProvider()` - Configure GitHub, GitLab, or Bitbucket
15+
- `listGitProviders()` - List configured providers
16+
- `deleteGitProvider()` - Remove a provider
17+
- `createPR()` - Create PR from generated code with audit trail
18+
- `listPRs()` - List PRs with filtering
19+
- `getPR()` - Get PR details
20+
- `syncPRStatus()` - Sync status from Git provider
21+
22+
- **New Types**: `GitProviderType`, `FileAction`, `CodeFile`, `CreatePRRequest`, `CreatePRResponse`, `PRRecord`, `ListPRsOptions`, `ListPRsResponse`
23+
24+
- **Supported Git Providers**:
25+
- GitHub (Cloud and Enterprise Server)
26+
- GitLab (Cloud and Self-Managed)
27+
- Bitbucket (Cloud and Server/Data Center)
28+
29+
---
30+
831
## [1.3.1] - 2025-12-29
932

1033
### Fixed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.getaxonflow</groupId>
88
<artifactId>axonflow-sdk</artifactId>
9-
<version>1.3.1</version>
9+
<version>1.4.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>AxonFlow Java SDK</name>

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

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.getaxonflow.sdk.exceptions.*;
1919
import com.getaxonflow.sdk.types.*;
20+
import com.getaxonflow.sdk.types.codegovernance.*;
2021
import com.getaxonflow.sdk.types.policies.PolicyTypes.*;
2122
import com.getaxonflow.sdk.util.*;
2223
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -1343,6 +1344,216 @@ private String extractErrorMessage(String body, String defaultMessage) {
13431344
return defaultMessage;
13441345
}
13451346

1347+
// ========================================================================
1348+
// Code Governance - Git Provider APIs (Enterprise)
1349+
// ========================================================================
1350+
1351+
/**
1352+
* Validates Git provider credentials without saving them.
1353+
*
1354+
* @param request the validation request with provider type and credentials
1355+
* @return validation result
1356+
* @throws IOException if the request fails
1357+
*/
1358+
public ValidateGitProviderResponse validateGitProvider(ValidateGitProviderRequest request) throws IOException {
1359+
logger.debug("Validating Git provider: {}", request.getType());
1360+
1361+
String json = objectMapper.writeValueAsString(request);
1362+
RequestBody body = RequestBody.create(json, JSON);
1363+
1364+
Request.Builder builder = new Request.Builder()
1365+
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers/validate")
1366+
.post(body);
1367+
1368+
addAuthHeaders(builder);
1369+
1370+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1371+
return parseResponse(response, ValidateGitProviderResponse.class);
1372+
}
1373+
}
1374+
1375+
/**
1376+
* Configures a Git provider for code governance.
1377+
*
1378+
* @param request the configuration request with provider type and credentials
1379+
* @return configuration result
1380+
* @throws IOException if the request fails
1381+
*/
1382+
public ConfigureGitProviderResponse configureGitProvider(ConfigureGitProviderRequest request) throws IOException {
1383+
logger.debug("Configuring Git provider: {}", request.getType());
1384+
1385+
String json = objectMapper.writeValueAsString(request);
1386+
RequestBody body = RequestBody.create(json, JSON);
1387+
1388+
Request.Builder builder = new Request.Builder()
1389+
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers")
1390+
.post(body);
1391+
1392+
addAuthHeaders(builder);
1393+
1394+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1395+
return parseResponse(response, ConfigureGitProviderResponse.class);
1396+
}
1397+
}
1398+
1399+
/**
1400+
* Lists configured Git providers.
1401+
*
1402+
* @return list of configured providers
1403+
* @throws IOException if the request fails
1404+
*/
1405+
public ListGitProvidersResponse listGitProviders() throws IOException {
1406+
logger.debug("Listing Git providers");
1407+
1408+
Request.Builder builder = new Request.Builder()
1409+
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers")
1410+
.get();
1411+
1412+
addAuthHeaders(builder);
1413+
1414+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1415+
return parseResponse(response, ListGitProvidersResponse.class);
1416+
}
1417+
}
1418+
1419+
/**
1420+
* Deletes a configured Git provider.
1421+
*
1422+
* @param providerType the provider type to delete
1423+
* @throws IOException if the request fails
1424+
*/
1425+
public void deleteGitProvider(GitProviderType providerType) throws IOException {
1426+
logger.debug("Deleting Git provider: {}", providerType);
1427+
1428+
Request.Builder builder = new Request.Builder()
1429+
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers/" + providerType.getValue())
1430+
.delete();
1431+
1432+
addAuthHeaders(builder);
1433+
1434+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1435+
handleErrorResponse(response);
1436+
}
1437+
}
1438+
1439+
/**
1440+
* Creates a Pull Request from LLM-generated code.
1441+
*
1442+
* @param request the PR creation request with repository info and files
1443+
* @return the created PR details
1444+
* @throws IOException if the request fails
1445+
*/
1446+
public CreatePRResponse createPR(CreatePRRequest request) throws IOException {
1447+
logger.debug("Creating PR: {} in {}/{}", request.getTitle(), request.getOwner(), request.getRepo());
1448+
1449+
String json = objectMapper.writeValueAsString(request);
1450+
RequestBody body = RequestBody.create(json, JSON);
1451+
1452+
Request.Builder builder = new Request.Builder()
1453+
.url(config.getAgentUrl() + "/api/v1/code-governance/prs")
1454+
.post(body);
1455+
1456+
addAuthHeaders(builder);
1457+
1458+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1459+
return parseResponse(response, CreatePRResponse.class);
1460+
}
1461+
}
1462+
1463+
/**
1464+
* Lists PRs with optional filtering.
1465+
*
1466+
* @param options filtering options (limit, offset, state)
1467+
* @return list of PRs
1468+
* @throws IOException if the request fails
1469+
*/
1470+
public ListPRsResponse listPRs(ListPRsOptions options) throws IOException {
1471+
logger.debug("Listing PRs");
1472+
1473+
StringBuilder url = new StringBuilder(config.getAgentUrl() + "/api/v1/code-governance/prs");
1474+
StringBuilder query = new StringBuilder();
1475+
1476+
if (options != null) {
1477+
if (options.getLimit() != null) {
1478+
appendQueryParam(query, "limit", String.valueOf(options.getLimit()));
1479+
}
1480+
if (options.getOffset() != null) {
1481+
appendQueryParam(query, "offset", String.valueOf(options.getOffset()));
1482+
}
1483+
if (options.getState() != null) {
1484+
appendQueryParam(query, "state", options.getState());
1485+
}
1486+
}
1487+
1488+
if (query.length() > 0) {
1489+
url.append("?").append(query);
1490+
}
1491+
1492+
Request.Builder builder = new Request.Builder()
1493+
.url(url.toString())
1494+
.get();
1495+
1496+
addAuthHeaders(builder);
1497+
1498+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1499+
return parseResponse(response, ListPRsResponse.class);
1500+
}
1501+
}
1502+
1503+
/**
1504+
* Lists PRs with default options.
1505+
*
1506+
* @return list of PRs
1507+
* @throws IOException if the request fails
1508+
*/
1509+
public ListPRsResponse listPRs() throws IOException {
1510+
return listPRs(null);
1511+
}
1512+
1513+
/**
1514+
* Gets a specific PR by ID.
1515+
*
1516+
* @param prId the PR record ID
1517+
* @return the PR record
1518+
* @throws IOException if the request fails
1519+
*/
1520+
public PRRecord getPR(String prId) throws IOException {
1521+
logger.debug("Getting PR: {}", prId);
1522+
1523+
Request.Builder builder = new Request.Builder()
1524+
.url(config.getAgentUrl() + "/api/v1/code-governance/prs/" + prId)
1525+
.get();
1526+
1527+
addAuthHeaders(builder);
1528+
1529+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1530+
return parseResponse(response, PRRecord.class);
1531+
}
1532+
}
1533+
1534+
/**
1535+
* Syncs PR status from the Git provider.
1536+
*
1537+
* @param prId the PR record ID to sync
1538+
* @return the updated PR record
1539+
* @throws IOException if the request fails
1540+
*/
1541+
public PRRecord syncPRStatus(String prId) throws IOException {
1542+
logger.debug("Syncing PR status: {}", prId);
1543+
1544+
RequestBody body = RequestBody.create("{}", JSON);
1545+
1546+
Request.Builder builder = new Request.Builder()
1547+
.url(config.getAgentUrl() + "/api/v1/code-governance/prs/" + prId + "/sync")
1548+
.post(body);
1549+
1550+
addAuthHeaders(builder);
1551+
1552+
try (Response response = httpClient.newCall(builder.build()).execute()) {
1553+
return parseResponse(response, PRRecord.class);
1554+
}
1555+
}
1556+
13461557
@Override
13471558
public void close() {
13481559
httpClient.dispatcher().executorService().shutdown();
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2025 AxonFlow
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.getaxonflow.sdk.types.codegovernance;
17+
18+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
import java.util.Objects;
22+
23+
/**
24+
* A code file to include in a PR.
25+
*/
26+
@JsonIgnoreProperties(ignoreUnknown = true)
27+
public final class CodeFile {
28+
29+
@JsonProperty("path")
30+
private final String path;
31+
32+
@JsonProperty("content")
33+
private final String content;
34+
35+
@JsonProperty("language")
36+
private final String language;
37+
38+
@JsonProperty("action")
39+
private final FileAction action;
40+
41+
public CodeFile(
42+
@JsonProperty("path") String path,
43+
@JsonProperty("content") String content,
44+
@JsonProperty("language") String language,
45+
@JsonProperty("action") FileAction action) {
46+
this.path = Objects.requireNonNull(path, "path is required");
47+
this.content = Objects.requireNonNull(content, "content is required");
48+
this.language = language;
49+
this.action = Objects.requireNonNull(action, "action is required");
50+
}
51+
52+
public String getPath() {
53+
return path;
54+
}
55+
56+
public String getContent() {
57+
return content;
58+
}
59+
60+
public String getLanguage() {
61+
return language;
62+
}
63+
64+
public FileAction getAction() {
65+
return action;
66+
}
67+
68+
public static Builder builder() {
69+
return new Builder();
70+
}
71+
72+
public static class Builder {
73+
private String path;
74+
private String content;
75+
private String language;
76+
private FileAction action;
77+
78+
public Builder path(String path) {
79+
this.path = path;
80+
return this;
81+
}
82+
83+
public Builder content(String content) {
84+
this.content = content;
85+
return this;
86+
}
87+
88+
public Builder language(String language) {
89+
this.language = language;
90+
return this;
91+
}
92+
93+
public Builder action(FileAction action) {
94+
this.action = action;
95+
return this;
96+
}
97+
98+
public CodeFile build() {
99+
return new CodeFile(path, content, language, action);
100+
}
101+
}
102+
103+
@Override
104+
public boolean equals(Object o) {
105+
if (this == o) return true;
106+
if (o == null || getClass() != o.getClass()) return false;
107+
CodeFile codeFile = (CodeFile) o;
108+
return Objects.equals(path, codeFile.path) &&
109+
Objects.equals(content, codeFile.content) &&
110+
Objects.equals(language, codeFile.language) &&
111+
action == codeFile.action;
112+
}
113+
114+
@Override
115+
public int hashCode() {
116+
return Objects.hash(path, content, language, action);
117+
}
118+
119+
@Override
120+
public String toString() {
121+
return "CodeFile{" +
122+
"path='" + path + '\'' +
123+
", language='" + language + '\'' +
124+
", action=" + action +
125+
'}';
126+
}
127+
}

0 commit comments

Comments
 (0)