Skip to content

Commit

Permalink
Add new endpoint /api/project_pull_requests/gitlab_report for GitLab …
Browse files Browse the repository at this point in the history
…codeclimate

The plugin [sonar-gitlab-plugin](https://github.com/javamachr/sonar-gitlab-plugin) is relying on `/api/issues/search` with _query-parameter_ `pullRequest` to retrieve information and generate json.

Unfortunately the `pullRequest` parameter isn't available for CE edition.

Thus I've created a new endpoint called `/api/project_pull_requests/gitlab_report` with 2 mands parameters

1. `project`
2. `pullRequest`

This endpoint will transform all `OPEN` issue to `GitLab codeclimate` json format.

Thus simply add following inside your `.gitlab-ci.yml` after the analysis to get report (adapt your variables name, only `CI_MERGE_REQUEST_IID` is predefined)

```
    - |
      curl -o gl-code-quality-report.json --header "Authorization: Bearer ${SONAR_TOKEN}" "${SONAR_HOST_URL}/api/project_pull_requests/gitlab_report?project=${SONAR_PROJECT_KEY}&pullRequest=${CI_MERGE_REQUEST_IID}"
  artifacts:
    reports:
      codequality: gl-code-quality-report.json
```

And tada is working
  • Loading branch information
kakawait committed Oct 7, 2024
1 parent a228d9f commit f7aabef
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.DeleteAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.ListAction;

import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.GitLabReportAction;
import org.sonar.api.CoreProperties;
import org.sonar.api.Plugin;
import org.sonar.api.PropertyType;
Expand Down Expand Up @@ -94,6 +95,7 @@ public void load(CoreExtension.Context context) {
ValidateBindingAction.class,
DeleteAction.class,
ListAction.class,
GitLabReportAction.class,
PullRequestWs.class,

GithubValidator.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action;

import org.apache.commons.codec.digest.DigestUtils;
import org.sonar.api.issue.IssueStatus;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
import org.sonar.db.component.BranchType;
import org.sonar.db.issue.IssueDao;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.rule.RuleDao;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UserSession;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.ListAction.checkPermission;

public class GitLabReportAction extends ProjectWsAction {

private static final String PULL_REQUEST_PARAMETER = "pullRequest";

private static final Map<String, String> ruleKeyToCheckName = new HashMap<>();

private final UserSession userSession;

@Autowired
public GitLabReportAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
super("gitlab_report", dbClient, componentFinder);
this.userSession = userSession;
}

@Override
protected void configureAction(WebService.NewAction action) {
action.createParam(PULL_REQUEST_PARAMETER).setRequired(true);
}

@Override
protected void handleProjectRequest(ProjectDto project, Request request, Response response, DbSession dbSession) {
checkPermission(project, userSession);

String pullRequestId = request.mandatoryParam(PULL_REQUEST_PARAMETER);

BranchDto pullRequest = getDbClient()
.branchDao()
.selectByPullRequestKey(dbSession, project.getUuid(), pullRequestId)
.filter(branch -> branch.getBranchType() == BranchType.PULL_REQUEST)
.orElseThrow(() -> new NotFoundException(
String.format("Pull request '%s' is not found for project '%s'", pullRequestId,
project.getKey())));

IssueDao issueDao = getDbClient().issueDao();
RuleDao ruleDao = getDbClient().ruleDao();
// selectOpenByComponentUuids seems to not work as expected
Set<String> issueKeys = issueDao.selectIssueKeysByComponentUuid(dbSession, pullRequest.getUuid());

JsonWriter writer = response.newJsonWriter();
writer.beginArray();
issueDao
.selectByKeys(dbSession, issueKeys)
.stream()
.filter(i -> i.getIssueStatus() == IssueStatus.OPEN)
.forEach(i -> buildGitLabIssue(dbSession, i, ruleDao, writer));
writer.endArray();
writer.close();
}

private void buildGitLabIssue(DbSession dbSession, IssueDto issue, RuleDao ruleDao, JsonWriter writer) {
writer.beginObject();
// https://docs.gitlab.com/ee/ci/testing/code_quality.html#implement-a-custom-tool
writer.prop("check_name", getCheckName(dbSession, ruleDao, issue.getRuleKey()));
writer.prop("fingerprint", DigestUtils.md5Hex(issue.getKey()));
writer.prop("description", issue.getMessage());
writer.prop("severity", issue.getSeverity().toLowerCase());
// Location
buildLocation(issue, writer);
writer.endObject();
}

private void buildLocation(IssueDto issue, JsonWriter jsonWriter) {
jsonWriter.name("location").beginObject();
jsonWriter.prop("path", issue.getFilePath());
jsonWriter.name("lines").beginObject();
jsonWriter.prop("begin", issue.getLine());
jsonWriter.prop("end", issue.getLine());
jsonWriter.endObject();
jsonWriter.endObject();
}

private String getCheckName(DbSession dbSession, RuleDao ruleDao, RuleKey ruleKey) {
return ruleKeyToCheckName.computeIfAbsent(ruleKey.rule(), k -> ruleDao
.selectByKey(dbSession, ruleKey)
.map(r -> r.getName().toLowerCase().replaceAll("[^a-zA-Z ]", "").replaceAll("\\s+", "-"))
.orElse(ruleKey.toString()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void handleProjectRequest(ProjectDto project, Request request, Response r
protoBufWriter.write(protobufResponse.build(), request, response);
}

private static void checkPermission(ProjectDto project, UserSession userSession) {
static void checkPermission(ProjectDto project, UserSession userSession) {
if (userSession.hasEntityPermission(UserRole.USER, project) ||
userSession.hasEntityPermission(UserRole.SCAN, project) ||
userSession.hasPermission(GlobalPermission.SCAN)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.PullRequestWs;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.DeleteAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.ListAction;
import com.github.mc1arke.sonarqube.plugin.server.pullrequest.ws.pullrequest.action.GitLabReportAction;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.sonar.api.Plugin;
Expand Down Expand Up @@ -142,6 +143,7 @@ void shouldAddExtensionsForServerSideLoad() {
eq(ValidateBindingAction.class),
eq(DeleteAction.class),
eq(ListAction.class),
eq(GitLabReportAction.class),
eq(PullRequestWs.class),
eq(GithubValidator.class),
eq(DefaultGraphqlProvider.class),
Expand Down

0 comments on commit f7aabef

Please sign in to comment.