Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete old summary discussions. #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
public class CommunityBranchPlugin implements Plugin, CoreExtension {

public static final String IMAGE_URL_BASE = "com.github.mc1arke.sonarqube.plugin.branch.image-url-base";
public static final String SUBCATEGORY_BRANCH = "Branch";
public static final String PR_DELETE_OLD_ANALYSIS_SUMMARY = "com.github.mc1arke.sonarqube.plugin.branch.pullrequest.summary.deleteOldDiscussions";

@Override
public String getName() {
Expand Down Expand Up @@ -157,6 +159,16 @@ public void load(CoreExtension.Context context) {
.build(),
MonoRepoFeature.class);

context.addExtensions(PropertyDefinition.builder(PR_DELETE_OLD_ANALYSIS_SUMMARY)
.category(CoreProperties.CATEGORY_GENERAL)
.subCategory(SUBCATEGORY_BRANCH)
.onQualifiers(Qualifiers.PROJECT)
.name("Delete old summary")
.description("Delete old summary discussions (Gitlab only).")
.type(PropertyType.BOOLEAN)
.defaultValue(String.valueOf(false))
.index(4)
.build());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public interface GitlabClient {

void addMergeRequestDiscussionNote(long projectId, long mergeRequestIid, String discussionId, String noteContent) throws IOException;

void deleteMergeRequestDiscussionNote(long projectId, long mergeRequestIid, String discussionId, long noteId) throws IOException;

void resolveMergeRequestDiscussion(long projectId, long mergeRequestIid, String discussionId) throws IOException;

void setMergeRequestPipelineStatus(long projectId, String commitRevision, PipelineStatus status) throws IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
Expand Down Expand Up @@ -126,6 +123,14 @@ public void addMergeRequestDiscussionNote(long projectId, long mergeRequestIid,
entity(httpPost, null, httpResponse -> validateResponse(httpResponse, 201, "Commit discussions note added"));
}

@Override
public void deleteMergeRequestDiscussionNote(long projectId, long mergeRequestIid, String discussionId, long noteId) throws IOException {
String noteIdUrl = String.format("%s/projects/%s/merge_requests/%s/discussions/%s/notes/%s", baseGitlabApiUrl, projectId, mergeRequestIid, discussionId, noteId);

HttpDelete httpDel = new HttpDelete(noteIdUrl);
entity(httpDel, null, httpResponse -> validateResponse(httpResponse, 204, "Discussion note deleted"));
}

@Override
public void resolveMergeRequestDiscussion(long projectId, long mergeRequestIid, String discussionId) throws IOException {
String discussionIdUrl = String.format("%s/projects/%s/merge_requests/%s/discussions/%s?resolved=true", baseGitlabApiUrl, projectId, mergeRequestIid, discussionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public DecorationResult decorateQualityGateStatus(AnalysisDetails analysis, AlmS
.filter(comment -> !projectAlmSettingDto.getMonorepo() || isCommentFromCurrentProject(comment, analysis.getAnalysisProjectKey()))
.collect(Collectors.toList());

deleteOldSummaryDiscussions(client, pullRequest, analysis);

List<String> commentKeysForOpenComments = closeOldDiscussionsAndExtractRemainingKeys(client,
user,
currentProjectSonarqubeComments,
Expand Down Expand Up @@ -110,6 +112,8 @@ public DecorationResult decorateQualityGateStatus(AnalysisDetails analysis, AlmS
return builder.build();
}

protected abstract void deleteOldSummaryDiscussions(C client, P pullRequest, AnalysisDetails analysisDetails);

protected abstract C createClient(AlmSettingDto almSettingDto, ProjectAlmSettingDto projectAlmSettingDto);

protected abstract Optional<String> createFrontEndUrl(P pullRequest, AnalysisDetails analysisDetails);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce.pullrequest.azuredevops;

import com.github.mc1arke.sonarqube.plugin.CommunityBranchPlugin;
import com.github.mc1arke.sonarqube.plugin.almclient.azuredevops.AzureDevopsClient;
import com.github.mc1arke.sonarqube.plugin.almclient.azuredevops.AzureDevopsClientFactory;
import com.github.mc1arke.sonarqube.plugin.almclient.azuredevops.model.Comment;
Expand Down Expand Up @@ -245,4 +246,11 @@ protected Optional<ProjectIssueIdentifier> parseIssueDetails(AzureDevopsClient c
return parseIssueDetails(client, note, "See in SonarQube", NOTE_MARKDOWN_LEGACY_SEE_LINK_PATTERN);
}

@Override
protected void deleteOldSummaryDiscussions(AzureDevopsClient client, PullRequest pullRequest, AnalysisDetails analysis){
if (analysis.getScannerProperty(CommunityBranchPlugin.PR_DELETE_OLD_ANALYSIS_SUMMARY)
.map(Boolean::parseBoolean)
.orElse(Boolean.FALSE))
throw new IllegalStateException("Delete old discussions is not supported for AzureDevOps");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.github.mc1arke.sonarqube.plugin.ce.pullrequest.gitlab;

import com.github.mc1arke.sonarqube.plugin.CommunityBranchPlugin;
import com.github.mc1arke.sonarqube.plugin.almclient.gitlab.GitlabClient;
import com.github.mc1arke.sonarqube.plugin.almclient.gitlab.GitlabClientFactory;
import com.github.mc1arke.sonarqube.plugin.almclient.gitlab.model.Commit;
Expand All @@ -36,15 +37,15 @@
import com.github.mc1arke.sonarqube.plugin.ce.pullrequest.report.AnalysisSummary;
import com.github.mc1arke.sonarqube.plugin.ce.pullrequest.report.ReportGenerator;
import org.sonar.api.ce.posttask.QualityGate;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import org.sonar.db.alm.setting.ProjectAlmSettingDto;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

public class GitlabMergeRequestDecorator extends DiscussionAwarePullRequestDecorator<GitlabClient, MergeRequest, User, Discussion, Note> {
Expand All @@ -56,6 +57,19 @@ public class GitlabMergeRequestDecorator extends DiscussionAwarePullRequestDecor
private final GitlabClientFactory gitlabClientFactory;
private final MarkdownFormatterFactory formatterFactory;

// string hints that a certain comment is indeed a summary
private static final Set<String> SUMMARY_COMMENT_HINTS = new HashSet<>(Arrays.asList(
"Analysis Details",
"Issue",
"Bug",
"Vulnerability",
"Code Smell",
"Coverage and Duplications",
"View in SonarQube"
));

private static final Logger LOGGER = Loggers.get(GitlabMergeRequestDecorator.class);

public GitlabMergeRequestDecorator(ScmInfoRepository scmInfoRepository, GitlabClientFactory gitlabClientFactory, ReportGenerator reportGenerator, MarkdownFormatterFactory formatterFactory) {
super(scmInfoRepository, reportGenerator);
this.gitlabClientFactory = gitlabClientFactory;
Expand Down Expand Up @@ -224,4 +238,62 @@ protected void resolveDiscussion(GitlabClient client, Discussion discussion, Mer
}
}

@Override
protected void deleteOldSummaryDiscussions(GitlabClient client, MergeRequest pullRequest, AnalysisDetails analysis) {
Boolean deleteSummaryResolved = analysis.getScannerProperty(CommunityBranchPlugin.PR_DELETE_OLD_ANALYSIS_SUMMARY)
.map(Boolean::parseBoolean)
.orElse(Boolean.FALSE);

LOGGER.info("PR_DELETE_ANALYSIS_SUMMARY: %s", deleteSummaryResolved.toString());

LOGGER.info(String.format("cleanOldSummaryNotes PR: %s", pullRequest.getIid() ));
User currentUser = getCurrentUser(client);

var discussions = this.getDiscussions(client, pullRequest).stream()
.filter(discussion -> isSummary(client, discussion))
.collect(Collectors.toList());

LOGGER.info(String.format("cleanOldSummaryNotes Discussions: %s", discussions.stream().count() ));

for (var discussion : discussions) {
if (getNotesForDiscussion(client, discussion).stream()
.filter(this::isUserNote)
.anyMatch(note -> !isNoteFromCurrentUser(note, currentUser))) {
LOGGER.info(String.format("cleanOldSummaryNotes Discussion: %s cannot be deleted because has new notes", discussion.getId() ));
}else{
try {
LOGGER.info(String.format("cleanOldSummaryNotes Deleting discussion: %s", discussion.getId() ));
deleteDiscussion(client, discussion, pullRequest);
} catch (IOException ex) {
throw new IllegalStateException("Could not delete Merge Request Summary discussion", ex);
}
}
}
}

private boolean isSummaryNote(GitlabClient client, Note note) {
String noteContent = getNoteContent(client, note);
return SUMMARY_COMMENT_HINTS.stream().allMatch(hint -> noteContent.contains(hint));
}

private boolean isSummary(GitlabClient client, Discussion discussion) {
return discussion.getNotes().stream().anyMatch(note -> isSummaryNote(client, note));
}

private void deleteDiscussion(GitlabClient client, Discussion discussion, MergeRequest pullRequest) throws IOException {
User currentUser = getCurrentUser(client);
var userNotes = discussion.getNotes().stream()
.filter(note -> isNoteFromCurrentUser(note, currentUser))
.collect(Collectors.toList());

for (var note: userNotes) {
LOGGER.info(String.format("Deleting discussion note_id: %s, %s", note.getId(), note.getBody()));
client.deleteMergeRequestDiscussionNote(
pullRequest.getTargetProjectId(),
pullRequest.getIid(),
discussion.getId(),
note.getId()
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package com.github.mc1arke.sonarqube.plugin.scanner;

import com.github.mc1arke.sonarqube.plugin.CommunityBranchPlugin;
import com.github.mc1arke.sonarqube.plugin.ce.pullrequest.gitlab.GitlabMergeRequestDecorator;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
Expand Down Expand Up @@ -54,6 +55,8 @@ public void execute(SensorContext sensorContext) {
Optional.ofNullable(system2.property(GitlabMergeRequestDecorator.PULLREQUEST_GITLAB_PIPELINE_ID)).ifPresent(
v -> sensorContext.addContextProperty(GitlabMergeRequestDecorator.PULLREQUEST_GITLAB_PIPELINE_ID, v));

Optional.ofNullable(system2.property(CommunityBranchPlugin.PR_DELETE_OLD_ANALYSIS_SUMMARY)).ifPresent(
v -> sensorContext.addContextProperty(CommunityBranchPlugin.PR_DELETE_OLD_ANALYSIS_SUMMARY, v));
}

}