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 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}
, and others that are direct calls using HttpClient.
To find out which API endpoints and types are supported, go to .
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();
}
}
.
SOCIAL SHARE CARD GENERATOR