Skip to content

Instantly share code, notes, and snippets.

@ukuhnhardt
Created May 18, 2017 08:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ukuhnhardt/9553f44c28590b908062cd1443ef4b13 to your computer and use it in GitHub Desktop.
Save ukuhnhardt/9553f44c28590b908062cd1443ef4b13 to your computer and use it in GitHub Desktop.
Rest Endpoint: PR Approval and digitial signature
@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();
}
@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);
}
// 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;
}
});
}
@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
}
}
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());
}
}
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));
}
}
String condition = makeMergeCondition(pullRequest, valueMap, autoMergers);
final boolean veto = !evaluateCondition(condition, valueMap);
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