Created
May 18, 2017 08:49
-
-
Save ukuhnhardt/9553f44c28590b908062cd1443ef4b13 to your computer and use it in GitHub Desktop.
Rest Endpoint: PR Approval and digitial signature
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@POST | |
@Path("/{projectKey}/{repoSlug}/{pullReqId}/sign") | |
@Consumes({MediaType.APPLICATION_JSON}) | |
@Produces({MediaType.APPLICATION_JSON}) | |
public Response signApprove(@PathParam("pullReqId") final long pullReqId, | |
@PathParam("projectKey") final String projectKey, | |
@PathParam("repoSlug") final String repoSlug, | |
final SignApprove req) { | |
if (userManager.authenticate(req.getSignapproveUser(), req.getSignapprovePwd())) { | |
int repoId = repositoryService.getBySlug(req.getProjectKey(), req.getRepoSlug()).getId(); | |
PullRequestParticipant signer = lockService.getPullRequestLock(ApprovalResource.class.getName()).withLock(repoId, pullReqId, new UncheckedOperation<PullRequestParticipant>() { | |
@Override | |
public PullRequestParticipant perform() { | |
return pullRequestService.setReviewerStatus(repoId, pullReqId, PullRequestParticipantStatus.APPROVED); | |
} | |
}); // may throw exception, that's ok | |
if (signer != null) { | |
// that's a custom event | |
eventPublisher.publish(new PullRequestSignatureEvent(this, PullRequestEventType.SIGNED, projectKey, repoSlug, signer.getUser().getId(), pullReqId, new Date().getTime())); | |
} | |
return Response.ok().build(); | |
} | |
Map<String, String> resonseMap = ImmutableMap.of("error", "User authentication failed."); | |
return Response.ok().entity(resonseMap).build(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@EventListener | |
public void onPullRequestSignatureEvent(final PullRequestSignatureEvent event){ | |
scheduledExecutorService.schedule(new Callable<Object>() { | |
@Override | |
public Object call() throws Exception { | |
List<PullRequestSignature> signatures = dao.getPullRequestSignatures(event.getProjectKey(), event.getRepoSlug(), event.getPullRequestId()); | |
signatures.add(new PullRequestSignature(event.getEventType(), event.getProjectKey(), event.getRepoSlug(), event.getPullRequestId(), event.getUid(), event.getTimestamp())); | |
dao.setPullRequestSignatures(event.getProjectKey(), event.getRepoSlug(), event.getPullRequestId(), signatures); | |
log.info("PR id " + event.getPullRequestId() + " signed and approved by user-id " + event.getUid()); | |
return null; | |
} | |
}, 200, TimeUnit.MILLISECONDS); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PullRequestApprovedEvent << based on pullRequestService.setReviewerStatus(..) earlier | |
@EventListener | |
public void onPullRequestApprovedEvent(PullRequestParticipantApprovedEvent event) { | |
final PullRequest pullRequest = event.getPullRequest(); | |
/* | |
* PullRequestApprovedEvent listener MUST create thread to call mergeManager.mergeWhenReviewersApproved. Can be handled async | |
*/ | |
executorService.submit(new Callable<Void>() { | |
@Override | |
public Void call() throws Exception { | |
mergeManager.mergeWhenReviewersApproved(pullRequest); | |
return null; | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Override | |
public void mergeWhenReviewersApproved(PullRequest pullRequest) { | |
if (!workzoneLicenseManager.isLicensed()) { | |
log.error("Workzone license invalid. Not merging PR"); | |
log.info(workzoneLicenseManager.getLicenseMessageHtml()); | |
return; | |
} | |
if (canMerge(pullRequest)) { // checks workzone merge condition, does NOT invoke merge-checks | |
mergePullRequest(pullRequest); // see below | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private void mergePullRequest(final PullRequest pullRequest) { | |
final Repository toRepository = pullRequest.getToRef().getRepository(); | |
final RestBranchAutoMergers autoMergers = getBranchAutoMergers(pullRequest); | |
if (autoMergers != null && autoMergers.getAutomergeUsers().size() > 0) { | |
log.info(String.format("Merging pull request %s with id %d AUTOMATICALLY - all reviewers have approved", pullRequest.getTitle(), pullRequest.getId())); | |
final ApplicationUser mergeUser = userService.getUserByName((String) autoMergers.getAutomergeUsers().get(0).get("name")); | |
PullRequest mergedPullRequest = lockService.getPullRequestLock(DefaultMergeManager.class.getName()).withLock(toRepository.getId(), pullRequest.getId(), | |
new UncheckedOperation<PullRequest>() { | |
@Override | |
public PullRequest perform() { | |
return securityService.impersonating(mergeUser, "auto merge on behalf of " + mergeUser.getName()).call(new UncheckedOperation<PullRequest>() { | |
@Override | |
public PullRequest perform() { | |
RefAccessRequest refAccessRequest = new RefAccessRequest.Builder(toRepository, RefAccessType.UPDATE) | |
.ref(new SimpleRef.Builder(pullRequest.getToRef()).type(StandardRefType.BRANCH) | |
.build()) | |
.build(); | |
if (!refRestrictionService.hasPermission(refAccessRequest)) { | |
log.warn("User " + mergeUser + "does not have privileges to merge PR #", pullRequest.getId()); | |
return pullRequest; | |
} | |
try { | |
/* re-fetching the PR>> */ String condition = makeMergeCondition(pullRequest, valueMap, autoMergers); | |
final boolean veto = !evaluateCondition(condition, valueMap); | |
PullRequest updatedPullRequest = pullRequestService.getById(toRepository.getId(), pullRequest.getId()); | |
PullRequestMergeRequest mergeRequest = new PullRequestMergeRequest.Builder(updatedPullRequest).build(); | |
/* DB ERROR in here >>> */ return pullRequestService.merge(mergeRequest); // >> invokes merge-checks | |
} catch (Exception e) { | |
log.warn(String.format("Pull Request # %s : Automatic merge not successful : %s", pullRequest.getId(), e.getMessage()), e); | |
return pullRequest; | |
} | |
} | |
}); | |
} | |
}); | |
log.info(String.format("Pull Request # %s : automatic merge result %s ", mergedPullRequest.getId(), mergedPullRequest.getState())); | |
} else { | |
if (log.isDebugEnabled()) log.debug("PR:{} - No automerge configuration for Proj:{} Repo:{}", pullRequest.getId(), toRepository.getProject().getKey(), toRepository.getSlug()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public ApprovalsMergeCheck(WorkzoneDao dao, MergeManager mergeManager) { | |
this.dao = dao; | |
this.mergeManager = mergeManager; | |
} | |
@Override | |
public void check(@Nonnull MergeRequest mergeRequest) { | |
PullRequest pullRequest = mergeRequest.getPullRequest(); | |
GlobalConfig config = dao.getGlobalConfig(null, null); | |
final boolean enableMergeConditionVeto = config.isEnableMergeConditionVeto(); | |
if (log.isDebugEnabled()) log.debug("Enforcing merge condition {}", enableMergeConditionVeto); | |
if (!enableMergeConditionVeto) { | |
return; | |
} | |
final ArrayList<String> messages = Lists.newArrayList(); | |
if (mergeManager.isVeto(pullRequest, messages)){ // checks workzone merge-conditions, including pullRequest.getParticipants | |
if (messages.size() == 0){ | |
messages.add("Workzone Merge Conditions not met"); | |
} | |
if (messages.size() == 1){ | |
messages.add(""); | |
} | |
mergeRequest.veto(messages.get(0), messages.get(1)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
String condition = makeMergeCondition(pullRequest, valueMap, autoMergers); | |
final boolean veto = !evaluateCondition(condition, valueMap); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private int getSignedApprovals(final PullRequest pullRequest) { | |
// DB ERROR happens here VVVV // | |
final Page<PullRequestParticipant> participants = pullRequestService.getParticipants(pullRequest.getToRef().getRepository().getId(), pullRequest.getId(), new PageRequestImpl(0, 999)); | |
final Stream<PullRequestParticipant> signedApprovals = participants | |
.stream() | |
.filter(p -> p.isApproved() && approvalManager.isUserSignapprover(pullRequest, p)); | |
return (int)signedApprovals.count(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment