SonarQube provides developers with static code analysis capabilities, allowing teams to identify and resolve code quality issues. It also includes an AI Fix feature that helps developers fix specific issues using the full power of AI.
The project sonarqube-bulk-ai-fix shows how to focus only on those issues with AI fixes as an example of how to easily connect to SonarQube Server API and show the fixes in SonarQube IDE.
In this article, we’ll explore how the project leverages the SonarQube API alongside Vaadin and Spring Boot to create an intuitive dashboard for AI fixing code issues.
Why SonarQube API?
SonarQube’s API provides extensive capabilities to interact with its core features programmatically, enabling:
- Fetching project and issue data
- Managing quality gates and rules
- Integrating with CI/CD pipelines
This project highlights the use of the API to identify issues and seamlessly process AI fixes. Let's examine the technical components.
Overview of the Architecture
The project architecture integrates:
- Spring Boot: Acts as the backend to manage business logic and handle API interactions.
- Vaadin: Provides a modern, web-based UI for the dashboard.
- SonarQube API: Fetches project issues and applies fixes programmatically.
Prerequisites
- SonarQube Server / SonarQube Cloud instance running with AI CodeFix feature enabled (> 10.7)
- IDE running (VSCode, IntelliJ)
- Container runtime (Podman, Docker)
Running SonarQube Server
If you don't have access to a SonarQube Cloud instance with CodeFix feature enabled, you can try this example locally. To do that,request an Enterprise free trial license.
The next step is to create a docker-compose file to have persistence in our analysis, otherwise, it would use a memory database, and every time you run the container, it will be empty.
version: "3"
services:
sonarqube:
image: sonarqube:enterprise
depends_on:
- db
environment:
SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
- ~/cacerts:/opt/java/openjdk/lib/security/cacerts
ports:
- "9000:9000"
db:
image: postgres:latest
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
And finally, run SonarQube with this command :
docker-compose -f ./docker-compose-sonarqube-postgre.yaml up
After a few seconds, you can open our browser on localhost:9000
and specify the new password (the default is admin/admin).
Now you should paste the contents of the trial license file we received and Voila!
Analysing our first project
In order to test the connection with real data, you would need to incorporate a project analysis in the SonarQube dashboard. To do this for a given Java project simply run this command in the project folder (considering it's a Maven Java application):
mvn clean verify sonar:sonar -Dsonar.projectKey=sample -Dsonar.host.url=http://localhost:9000 -Dsonar.login=admin -Dsonar.password={replace with your pass}
Using the SonarQube API
The first step is connecting to the SonarQube API to retrieve and manipulate issue data. In the project, a SonarQubeClient
class encapsulates this functionality.
There are some API calls supported by the Sonar SDK, and others that are direct calls using HttpClient.
To find out which API endpoints and types are supported, go to Web API and Web API v2.
Sonar API Client in Spring Boot
You can use the sonar-ws
SDK to connect to SonarQube and get the issues for a given filter.
We need to use pagination, as it's only returning 100 elements per query.
public SearchWsResponse getListOfIssues(String project, String page, String pageSize) {
// Call the SonarQube API to get the issues
var httpConnector = HttpConnector.newBuilder()
.url(filter.sonarqubeUrl())
.credentials(filter.sonarqubeUser(), filter.sonarqubePassword())
.build();
var wsClient = WsClientFactories.getDefault().newClient(httpConnector);
var issueRequest = new SearchRequest();
issueRequest.setProjects(Collections.singletonList(project));
if (filter.severity() != null) {
issueRequest.setSeverities(List.of(filter.severity()));
}
issueRequest.setP(page);
issueRequest.setPs(pageSize);
return wsClient.issues().search(issueRequest);
}
To check if an issue has an AI Code Fix, you need to make a direct call using the HttpRequest object because the SDK doesn't yet cover this call.
// check if the issue has code fix suggestions
HttpRequest requestCheckIfIssueHasCodeFix = HttpRequest.newBuilder()
.uri(URI.create(urlFixSuggestionsIssues))
.header("Authorization", getAuthorization())
.build();
HttpResponse<String> response = client.send(requestCheckIfIssueHasCodeFix, BodyHandlers.ofString());
if (response.body().contains("\"aiSuggestion\":\"AVAILABLE\"")) {
issuesWithCodeFix.add(issue);
}
Interacting with the IDE
One feature of this Dashboard is the ability to send the AI Fix to the local running instance of an IDE and commit the change to our code base.
SonarQube for IDE has an API you can use to send pieces of code that need to be refactored.
public void sendCodeFixToSonarLint(int port, Issue issue, AISuggestion issueCodeFix, String codeFile)
throws JsonProcessingException {
var uri = URI.create("http://localhost:" + port + SONARLINT_API_FIX +
"?server=" + URLEncoder.encode(filter.sonarqubeUrl(), StandardCharsets.UTF_8) +
"&project=" + filter.project() +
"&issue=" + issueCodeFix.issueId() +
"&branch=master");
var codeFileLines = codeFile.split("\n");
var sonarLintSuggestion = new SonarLintSuggestion(
issueCodeFix.explanation(),
new SonarLintSuggestion.FileEdit(
issueCodeFix.changes().stream().map(change -> new SonarLintSuggestion.FileEdit.Change(
change.newCode(),
String.join("\n",
Arrays.copyOfRange(codeFileLines, change.startLine() - 1, change.endLine())),
new SonarLintSuggestion.FileEdit.Change.LineRange(
change.startLine(),
change.endLine())))
.toList(),
getFileFromComponent(issue.getComponent())),
issueCodeFix.id());
HttpRequest sendCodeFixToSonarLintRequest = HttpRequest.newBuilder()
.uri(URI.create(uri.toString()))
.POST(BodyPublishers.ofString(new ObjectMapper().writeValueAsString(sonarLintSuggestion)))
.build();
HttpClient client = HttpClient.newHttpClient();
try {
HttpResponse<String> response = client.send(sendCodeFixToSonarLintRequest, BodyHandlers.ofString());
System.out.println(response.request().toString() + "\n" + response.body());
writeToFileAppliedCodeFix(issueCodeFix);
Notification.show("Response from SonarLint: " + response.statusCode() + "\n" + response.body());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
Building the UI with Vaadin
Vaadin simplifies the process of building web UIs in Java by combining a component-based framework with server-side logic.
The process is to create different components and add them to layout containers.
Here’s how the project creates a dashboard page:
Vaadin Page for the dashboard
Adding the components to the layouts :
public IssuesDashboard() {
sonarqubePanel = new HorizontalLayout();
sonarqubeUrlEdit = new TextField("SonarQube Server URL");
sonarqubeUrlEdit.setValue("http://localhost:9000");
sonarqubeUserEdit = new TextField("User");
sonarqubePasswordEdit = new TextField("Password");
sonarqubePanel.add(sonarqubeUrlEdit, sonarqubeUserEdit, sonarqubePasswordEdit);
}
Listeners to button actions :
applyFixesButton = new Button("Send Selected Fix to SonarQube IDE");
applyFixesButton.addClickListener(e -> {
Notification.show("Processing fixes");
applyFixes();
Notification.show("Processing fixes finished");
});
Bringing It All Together
To run the project:
-
Start the Spring Boot application:
./mvnw spring-boot:run
Access the Vaadin UI at
http://localhost:8080
.
You can then enter the project key, fetch issues, and explore the automation possibilities.
Conclusion
The sonarqube-bulk-ai-fix
project demonstrates the power of combining the SonarQube API, Vaadin, and Spring Boot to create a user-friendly and efficient dashboard for AI code fixes. Whether you're looking to streamline your workflows or explore innovative ways to interact with SonarQube, this project provides an excellent start point.
Check out the full source code and contribute to the project on GitHub.
Top comments (0)