Skip to content

Commit

Permalink
Add support of summaryComment on GitHub
Browse files Browse the repository at this point in the history
  • Loading branch information
VanRoy authored and mc1arke committed Mar 30, 2021
1 parent 87a4560 commit a8dca45
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 Julien Roy
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.ce.pullrequest.github.v4;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.aexp.nodes.graphql.annotations.GraphQLArgument;
import io.aexp.nodes.graphql.annotations.GraphQLProperty;

@GraphQLProperty(name = "addComment", arguments = {@GraphQLArgument(name = "input")})
public class AddComment {

private final String clientMutationId;

@JsonCreator
public AddComment(@JsonProperty("clientMutationId") String clientMutationId) {
this.clientMutationId = clientMutationId;
}

public String getClientMutationId() {
return clientMutationId;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2021 Julien Roy
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
package com.github.mc1arke.sonarqube.plugin.ce.pullrequest.github.v4;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.aexp.nodes.graphql.annotations.GraphQLArgument;
import io.aexp.nodes.graphql.annotations.GraphQLProperty;

@GraphQLProperty(name = "repository", arguments = {@GraphQLArgument(name = "owner"), @GraphQLArgument(name = "name")})
public class GetPullRequest {

private final String url;

@GraphQLProperty(name = "pullRequest", arguments = {@GraphQLArgument(name = "number")})
private final PullRequest pullRequest;

@JsonCreator
public GetPullRequest(@JsonProperty("url") String url, @JsonProperty("pullRequest") PullRequest pullRequest) {
this.url = url;
this.pullRequest = pullRequest;
}

public String getUrl() {
return url;
}

public PullRequest getPullRequest() {
return pullRequest;
}

public static class PullRequest {

private final String id;

@JsonCreator
public PullRequest(@JsonProperty("id") String id) {
this.id = id;
}

public String getId() {
return id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public DecorationResult createCheckRun(AnalysisDetails analysisDetails, AlmSetti
headers.put("Accept", "application/vnd.github.antiope-preview+json");


String summary = analysisDetails.createAnalysisSummary(new MarkdownFormatterFactory());

List<PostAnalysisIssueVisitor.ComponentIssue> issues = analysisDetails.getPostAnalysisIssueVisitor().getIssues();

List<InputObject<Object>> annotations = createAnnotations(issues);
Expand All @@ -119,7 +121,7 @@ public DecorationResult createCheckRun(AnalysisDetails analysisDetails, AlmSetti
QualityGate.Status.OK ?
"success" :
"failed"))
.put("summary", analysisDetails.createAnalysisSummary(new MarkdownFormatterFactory()))
.put("summary", summary)
.put("annotations", annotations);

SimpleDateFormat startedDateFormat = new SimpleDateFormat(DATE_TIME_PATTERN);
Expand Down Expand Up @@ -165,12 +167,61 @@ public DecorationResult createCheckRun(AnalysisDetails analysisDetails, AlmSetti
inputObjectArguments, checkRunOutputContentBuilder, graphQLRequestEntityBuilder);


Optional<String> oPullRequestKey = analysisDetails.getPullRequestKey();
if (Optional.ofNullable(projectAlmSettingDto.getSummaryCommentEnabled()).orElse(true) && oPullRequestKey.isPresent()) {
postSummaryComment(apiUrl, headers, projectPath, oPullRequestKey.get(), summary);
}

return DecorationResult.builder()
.withPullRequestUrl(repositoryAuthenticationToken.getRepositoryUrl() + "/pull/" + analysisDetails.getBranchName())
.build();

}

private void postSummaryComment(String apiUrl, Map<String, String> headers, String projectPath, String pullRequestKey, String summary) throws IOException {

String[] paths = projectPath.split("/", 2);
String owner = paths[0];
String projectName = paths[1];

GraphQLRequestEntity getPullRequest =
graphqlProvider.createRequestBuilder()
.url(getGraphqlUrl(apiUrl))
.headers(headers)
.request(GetPullRequest.class)
.arguments(
new Arguments("repository", new Argument<>("owner", owner), new Argument<>("name", projectName)),
new Arguments("repository.pullRequest", new Argument<>("number", Integer.valueOf(pullRequestKey)))
)
.requestMethod(GraphQLTemplate.GraphQLMethod.QUERY)
.build();


GraphQLResponseEntity<GetPullRequest> response =
executeRequest((r, t) -> graphqlProvider.createGraphQLTemplate().execute(r, t), getPullRequest, GetPullRequest.class);

String pullRequestId = response.getResponse().getPullRequest().getId();

InputObject.Builder<Object> repositoryInputObjectBuilder = graphqlProvider.createInputObject();

InputObject<Object> input = repositoryInputObjectBuilder
.put("body", summary)
.put("subjectId", pullRequestId)
.build();

GraphQLRequestEntity graphQLRequestEntity =
graphqlProvider.createRequestBuilder()
.url(getGraphqlUrl(apiUrl))
.headers(headers)
.request(AddComment.class)
.arguments(new Arguments("addComment", new Argument<>("input", input)))
.requestMethod(GraphQLTemplate.GraphQLMethod.MUTATE)
.build();

executeRequest((r, t) -> graphqlProvider.createGraphQLTemplate().mutate(r, t), graphQLRequestEntity, AddComment.class);

}

private static <R> GraphQLResponseEntity<R> executeRequest(
BiFunction<GraphQLRequestEntity, Class<R>, GraphQLResponseEntity<R>> executor, GraphQLRequestEntity graphQLRequestEntity, Class<R> responseType) {
LOGGER.debug("Using request: " + graphQLRequestEntity.getRequest());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
public class SetGithubBindingAction extends SetBindingAction {

private static final String REPOSITORY_PARAMETER = "repository";
private static final String SUMMARY_COMMENT_PARAMETER = "summaryCommentEnabled";

public SetGithubBindingAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession) {
super(dbClient, componentFinder, userSession, "set_github_binding");
Expand All @@ -37,6 +38,7 @@ public SetGithubBindingAction(DbClient dbClient, ComponentFinder componentFinder
protected void configureAction(WebService.NewAction action) {
super.configureAction(action);
action.createParam(REPOSITORY_PARAMETER).setRequired(true).setMaximumLength(256);
action.createParam(SUMMARY_COMMENT_PARAMETER).setRequired(false).setBooleanPossibleValues();
}

@Override
Expand All @@ -45,6 +47,7 @@ protected ProjectAlmSettingDto createProjectAlmSettingDto(String projectUuid, St
.setProjectUuid(projectUuid)
.setAlmSettingUuid(settingsUuid)
.setAlmRepo(request.mandatoryParam(REPOSITORY_PARAMETER))
.setSummaryCommentEnabled(request.paramAsBoolean(SUMMARY_COMMENT_PARAMETER))
.setMonorepo(false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ private void createCheckRunHappyPath(QualityGate.Status status, String basePath,
when(analysisDetails.getBranchName()).thenReturn("branchName");
when(analysisDetails.getAnalysisDate()).thenReturn(new Date(1234567890));
when(analysisDetails.getAnalysisId()).thenReturn("analysis ID");
when(analysisDetails.getPullRequestKey()).thenReturn(Optional.of("1"));
when(analysisDetails.getPostAnalysisIssueVisitor()).thenReturn(postAnalysisIssueVisitor);

ArgumentCaptor<String> authenticationProviderArgumentCaptor = ArgumentCaptor.forClass(String.class);
Expand Down Expand Up @@ -398,18 +399,37 @@ private void createCheckRunHappyPath(QualityGate.Status status, String basePath,

ObjectMapper objectMapper = new ObjectMapper();
GraphQLResponseEntity<CreateCheckRun> graphQLResponseEntity =
new ObjectMapper().readValue("{\"response\": {\"checkRun\": {\"id\": \"ABC\"}}}", objectMapper.getTypeFactory().constructParametricType(GraphQLResponseEntity.class, CreateCheckRun.class));
objectMapper.readValue("{\"response\": {\"checkRun\": {\"id\": \"ABC\"}}}", objectMapper.getTypeFactory().constructParametricType(GraphQLResponseEntity.class, CreateCheckRun.class));

ArgumentCaptor<GraphQLRequestEntity> requestEntityArgumentCaptor =
ArgumentCaptor.forClass(GraphQLRequestEntity.class);
ArgumentCaptor<GraphQLRequestEntity> requestEntityArgumentCaptor = ArgumentCaptor.forClass(GraphQLRequestEntity.class);

GraphQLTemplate graphQLTemplate = mock(GraphQLTemplate.class);
when(graphQLTemplate.mutate(requestEntityArgumentCaptor.capture(), eq(CreateCheckRun.class)))
.thenReturn(graphQLResponseEntity);
when(graphQLTemplate.mutate(requestEntityArgumentCaptor.capture(), eq(CreateCheckRun.class))).thenReturn(graphQLResponseEntity);

GraphQLResponseEntity<GetPullRequest> getPullRequestResponseEntity =
objectMapper.readValue("{" +
"\"response\": " +
" {\n" +
" \"pullRequest\": {\n" +
" \"id\": \"MDExOlB1bGxSZXF1ZXN0MzUzNDc=\"\n" +
" }\n" +
" }\n" +
"}", objectMapper.getTypeFactory().constructParametricType(GraphQLResponseEntity.class, GetPullRequest.class));

ArgumentCaptor<GraphQLRequestEntity> getPullRequestRequestEntityArgumentCaptor = ArgumentCaptor.forClass(GraphQLRequestEntity.class);
when(graphQLTemplate.execute(getPullRequestRequestEntityArgumentCaptor.capture(), eq(GetPullRequest.class))).thenReturn(getPullRequestResponseEntity);

GraphQLResponseEntity<AddComment> addCommentResponseEntity =
objectMapper.readValue("{\"response\":{}}", objectMapper.getTypeFactory().constructParametricType(GraphQLResponseEntity.class, AddComment.class));

ArgumentCaptor<GraphQLRequestEntity> addCommentRequestEntityArgumentCaptor = ArgumentCaptor.forClass(GraphQLRequestEntity.class);
when(graphQLTemplate.mutate(addCommentRequestEntityArgumentCaptor.capture(), eq(AddComment.class))).thenReturn(addCommentResponseEntity);

when(graphqlProvider.createGraphQLTemplate()).thenReturn(graphQLTemplate);

ProjectAlmSettingDto projectAlmSettingDto = mock(ProjectAlmSettingDto.class);
when(projectAlmSettingDto.getAlmRepo()).thenReturn("dummy/repo");
when(projectAlmSettingDto.getSummaryCommentEnabled()).thenReturn(true);
AlmSettingDto almSettingDto = mock(AlmSettingDto.class);
when(almSettingDto.getUrl()).thenReturn(basePath);
when(almSettingDto.getAppId()).thenReturn("app id");
Expand All @@ -419,7 +439,7 @@ private void createCheckRunHappyPath(QualityGate.Status status, String basePath,
new GraphqlCheckRunProvider(graphqlProvider, clock, githubApplicationAuthenticationProvider, server);
testCase.createCheckRun(analysisDetails, almSettingDto, projectAlmSettingDto);

assertEquals(1, requestBuilders.size());
assertEquals(3, requestBuilders.size());

Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer dummyAuthToken");
Expand Down Expand Up @@ -473,7 +493,7 @@ private void createCheckRunHappyPath(QualityGate.Status status, String basePath,
position++;
}

assertEquals(2 + position, inputObjectBuilders.size());
assertEquals(3 + position, inputObjectBuilders.size());

ArgumentCaptor<List<InputObject<Object>>> annotationArgumentCaptor = ArgumentCaptor.forClass(List.class);

Expand All @@ -499,6 +519,25 @@ private void createCheckRunHappyPath(QualityGate.Status status, String basePath,
verify(inputObjectBuilders.get(position + 1)).put(eq("externalId"), eq("analysis ID"));
verify(inputObjectBuilders.get(position + 1)).put(eq("output"), eq(inputObjects.get(position)));
verify(inputObjectBuilders.get(position + 1)).build();

// Verify getPullRequest requestEntity
assertEquals(
"query { repository (owner:\"dummy\",name:\"repo\") { url pullRequest : pullRequest (number:1) { id } } } ",
getPullRequestRequestEntityArgumentCaptor.getValue().getRequest()
);

// Validate AddComment
verify(requestBuilders.get(2)).url(fullPath);
verify(requestBuilders.get(2)).headers(headers);
verify(requestBuilders.get(2)).requestMethod(GraphQLTemplate.GraphQLMethod.MUTATE);
verify(requestBuilders.get(2)).build();
assertEquals(requestEntities.get(2), addCommentRequestEntityArgumentCaptor.getValue());

assertEquals(
"mutation { addComment (input:{body:\"dummy summary\",subjectId:\"MDExOlB1bGxSZXF1ZXN0MzUzNDc=\"}) { clientMutationId } } ",
addCommentRequestEntityArgumentCaptor.getValue().getRequest()
);

}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public void testConfigureAction() {
when(repositoryParameter.setRequired(anyBoolean())).thenReturn(repositoryParameter);
when(newAction.createParam("repository")).thenReturn(repositoryParameter);

WebService.NewParam commentEnabledParameter = mock(WebService.NewParam.class);
when(commentEnabledParameter.setBooleanPossibleValues()).thenReturn(commentEnabledParameter);
when(commentEnabledParameter.setRequired(anyBoolean())).thenReturn(commentEnabledParameter);
when(newAction.createParam("summaryCommentEnabled")).thenReturn(commentEnabledParameter);

WebService.NewParam almSettingParameter = mock(WebService.NewParam.class);
when(almSettingParameter.setMaximumLength(any(Integer.class))).thenReturn(almSettingParameter);
when(almSettingParameter.setRequired(anyBoolean())).thenReturn(almSettingParameter);
Expand All @@ -68,11 +73,12 @@ public void testCreateProjectAlmSettingDto() {

Request request = mock(Request.class);
when(request.mandatoryParam("repository")).thenReturn("repository");
when(request.paramAsBoolean("summaryCommentEnabled")).thenReturn(true);

SetGithubBindingAction testCase = new SetGithubBindingAction(dbClient, componentFinder, userSession);
ProjectAlmSettingDto result = testCase.createProjectAlmSettingDto("projectUuid", "settingsUuid", request);

assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setMonorepo(false));
assertThat(result).isEqualToComparingFieldByField(new ProjectAlmSettingDto().setProjectUuid("projectUuid").setAlmSettingUuid("settingsUuid").setAlmRepo("repository").setSummaryCommentEnabled(true).setMonorepo(false));

}
}

0 comments on commit a8dca45

Please sign in to comment.