Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to the AxonFlow Java SDK will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.4.0] - 2025-12-29

### Added

- **Code Governance Git Provider APIs** (Enterprise): Create 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

- **New Types**: `GitProviderType`, `FileAction`, `CodeFile`, `CreatePRRequest`, `CreatePRResponse`, `PRRecord`, `ListPRsOptions`, `ListPRsResponse`

- **Supported Git Providers**:
- GitHub (Cloud and Enterprise Server)
- GitLab (Cloud and Self-Managed)
- Bitbucket (Cloud and Server/Data Center)

---

## [1.3.1] - 2025-12-29

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.getaxonflow</groupId>
<artifactId>axonflow-sdk</artifactId>
<version>1.3.1</version>
<version>1.4.0</version>
<packaging>jar</packaging>

<name>AxonFlow Java SDK</name>
Expand Down
211 changes: 211 additions & 0 deletions src/main/java/com/getaxonflow/sdk/AxonFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.getaxonflow.sdk.exceptions.*;
import com.getaxonflow.sdk.types.*;
import com.getaxonflow.sdk.types.codegovernance.*;
import com.getaxonflow.sdk.types.policies.PolicyTypes.*;
import com.getaxonflow.sdk.util.*;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand Down Expand Up @@ -1343,6 +1344,216 @@ private String extractErrorMessage(String body, String defaultMessage) {
return defaultMessage;
}

// ========================================================================
// Code Governance - Git Provider APIs (Enterprise)
// ========================================================================

/**
* Validates Git provider credentials without saving them.
*
* @param request the validation request with provider type and credentials
* @return validation result
* @throws IOException if the request fails
*/
public ValidateGitProviderResponse validateGitProvider(ValidateGitProviderRequest request) throws IOException {
logger.debug("Validating Git provider: {}", request.getType());

String json = objectMapper.writeValueAsString(request);
RequestBody body = RequestBody.create(json, JSON);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers/validate")
.post(body);

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, ValidateGitProviderResponse.class);
}
}

/**
* Configures a Git provider for code governance.
*
* @param request the configuration request with provider type and credentials
* @return configuration result
* @throws IOException if the request fails
*/
public ConfigureGitProviderResponse configureGitProvider(ConfigureGitProviderRequest request) throws IOException {
logger.debug("Configuring Git provider: {}", request.getType());

String json = objectMapper.writeValueAsString(request);
RequestBody body = RequestBody.create(json, JSON);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers")
.post(body);

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, ConfigureGitProviderResponse.class);
}
}

/**
* Lists configured Git providers.
*
* @return list of configured providers
* @throws IOException if the request fails
*/
public ListGitProvidersResponse listGitProviders() throws IOException {
logger.debug("Listing Git providers");

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers")
.get();

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, ListGitProvidersResponse.class);
}
}

/**
* Deletes a configured Git provider.
*
* @param providerType the provider type to delete
* @throws IOException if the request fails
*/
public void deleteGitProvider(GitProviderType providerType) throws IOException {
logger.debug("Deleting Git provider: {}", providerType);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/git-providers/" + providerType.getValue())
.delete();

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
handleErrorResponse(response);
}
}

/**
* Creates a Pull Request from LLM-generated code.
*
* @param request the PR creation request with repository info and files
* @return the created PR details
* @throws IOException if the request fails
*/
public CreatePRResponse createPR(CreatePRRequest request) throws IOException {
logger.debug("Creating PR: {} in {}/{}", request.getTitle(), request.getOwner(), request.getRepo());

String json = objectMapper.writeValueAsString(request);
RequestBody body = RequestBody.create(json, JSON);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/prs")
.post(body);

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, CreatePRResponse.class);
}
}

/**
* Lists PRs with optional filtering.
*
* @param options filtering options (limit, offset, state)
* @return list of PRs
* @throws IOException if the request fails
*/
public ListPRsResponse listPRs(ListPRsOptions options) throws IOException {
logger.debug("Listing PRs");

StringBuilder url = new StringBuilder(config.getAgentUrl() + "/api/v1/code-governance/prs");
StringBuilder query = new StringBuilder();

if (options != null) {
if (options.getLimit() != null) {
appendQueryParam(query, "limit", String.valueOf(options.getLimit()));
}
if (options.getOffset() != null) {
appendQueryParam(query, "offset", String.valueOf(options.getOffset()));
}
if (options.getState() != null) {
appendQueryParam(query, "state", options.getState());
}
}

if (query.length() > 0) {
url.append("?").append(query);
}

Request.Builder builder = new Request.Builder()
.url(url.toString())
.get();

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, ListPRsResponse.class);
}
}

/**
* Lists PRs with default options.
*
* @return list of PRs
* @throws IOException if the request fails
*/
public ListPRsResponse listPRs() throws IOException {
return listPRs(null);
}

/**
* Gets a specific PR by ID.
*
* @param prId the PR record ID
* @return the PR record
* @throws IOException if the request fails
*/
public PRRecord getPR(String prId) throws IOException {
logger.debug("Getting PR: {}", prId);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/prs/" + prId)
.get();

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, PRRecord.class);
}
}

/**
* Syncs PR status from the Git provider.
*
* @param prId the PR record ID to sync
* @return the updated PR record
* @throws IOException if the request fails
*/
public PRRecord syncPRStatus(String prId) throws IOException {
logger.debug("Syncing PR status: {}", prId);

RequestBody body = RequestBody.create("{}", JSON);

Request.Builder builder = new Request.Builder()
.url(config.getAgentUrl() + "/api/v1/code-governance/prs/" + prId + "/sync")
.post(body);

addAuthHeaders(builder);

try (Response response = httpClient.newCall(builder.build()).execute()) {
return parseResponse(response, PRRecord.class);
}
}

@Override
public void close() {
httpClient.dispatcher().executorService().shutdown();
Expand Down
127 changes: 127 additions & 0 deletions src/main/java/com/getaxonflow/sdk/types/codegovernance/CodeFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2025 AxonFlow
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.getaxonflow.sdk.types.codegovernance;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Objects;

/**
* A code file to include in a PR.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class CodeFile {

@JsonProperty("path")
private final String path;

@JsonProperty("content")
private final String content;

@JsonProperty("language")
private final String language;

@JsonProperty("action")
private final FileAction action;

public CodeFile(
@JsonProperty("path") String path,
@JsonProperty("content") String content,
@JsonProperty("language") String language,
@JsonProperty("action") FileAction action) {
this.path = Objects.requireNonNull(path, "path is required");
this.content = Objects.requireNonNull(content, "content is required");
this.language = language;
this.action = Objects.requireNonNull(action, "action is required");
}

public String getPath() {
return path;
}

public String getContent() {
return content;
}

public String getLanguage() {
return language;
}

public FileAction getAction() {
return action;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private String path;
private String content;
private String language;
private FileAction action;

public Builder path(String path) {
this.path = path;
return this;
}

public Builder content(String content) {
this.content = content;
return this;
}

public Builder language(String language) {
this.language = language;
return this;
}

public Builder action(FileAction action) {
this.action = action;
return this;
}

public CodeFile build() {
return new CodeFile(path, content, language, action);
}
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CodeFile codeFile = (CodeFile) o;
return Objects.equals(path, codeFile.path) &&
Objects.equals(content, codeFile.content) &&
Objects.equals(language, codeFile.language) &&
action == codeFile.action;
}

@Override
public int hashCode() {
return Objects.hash(path, content, language, action);
}

@Override
public String toString() {
return "CodeFile{" +
"path='" + path + '\'' +
", language='" + language + '\'' +
", action=" + action +
'}';
}
}
Loading
Loading