package se.bjurr.prnfb.listener; import static com.google.common.base.Optional.absent; import static com.google.common.base.Optional.of; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static java.util.regex.Pattern.compile; import static org.slf4j.LoggerFactory.getLogger; import static se.bjurr.prnfb.http.UrlInvoker.urlInvoker; import static se.bjurr.prnfb.listener.PrnfbPullRequestAction.fromPullRequestEvent; import static se.bjurr.prnfb.settings.TRIGGER_IF_MERGE.ALWAYS; import static se.bjurr.prnfb.settings.TRIGGER_IF_MERGE.CONFLICTING; import static se.bjurr.prnfb.settings.TRIGGER_IF_MERGE.NOT_CONFLICTING; import java.util.concurrent.ExecutorService; import org.slf4j.Logger; import com.atlassian.bitbucket.event.pull.PullRequestCommentAddedEvent; import com.atlassian.bitbucket.event.pull.PullRequestCommentDeletedEvent; import com.atlassian.bitbucket.event.pull.PullRequestCommentEditedEvent; import com.atlassian.bitbucket.event.pull.PullRequestCommentEvent; import com.atlassian.bitbucket.event.pull.PullRequestCommentRepliedEvent; import com.atlassian.bitbucket.event.pull.PullRequestDeclinedEvent; import com.atlassian.bitbucket.event.pull.PullRequestEvent; import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent; import com.atlassian.bitbucket.event.pull.PullRequestOpenedEvent; import com.atlassian.bitbucket.event.pull.PullRequestParticipantStatusUpdatedEvent; import com.atlassian.bitbucket.event.pull.PullRequestReopenedEvent; import com.atlassian.bitbucket.event.pull.PullRequestRescopedEvent; import com.atlassian.bitbucket.event.pull.PullRequestUpdatedEvent; import com.atlassian.bitbucket.pull.PullRequest; import com.atlassian.bitbucket.pull.PullRequestService; import com.atlassian.event.api.EventListener; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import se.bjurr.prnfb.http.ClientKeyStore; import se.bjurr.prnfb.http.HttpResponse; import se.bjurr.prnfb.http.Invoker; import se.bjurr.prnfb.http.NotificationResponse; import se.bjurr.prnfb.http.UrlInvoker; import se.bjurr.prnfb.service.PrnfbRenderer; import se.bjurr.prnfb.service.PrnfbRenderer.ENCODE_FOR; import se.bjurr.prnfb.service.PrnfbRendererFactory; import se.bjurr.prnfb.service.SettingsService; import se.bjurr.prnfb.service.VariablesContext; import se.bjurr.prnfb.service.VariablesContext.VariablesContextBuilder; import se.bjurr.prnfb.settings.PrnfbHeader; import se.bjurr.prnfb.settings.PrnfbNotification; import se.bjurr.prnfb.settings.PrnfbSettingsData; import se.bjurr.prnfb.settings.TRIGGER_IF_MERGE; public class PrnfbPullRequestEventListener { private static final Logger LOG = getLogger(PrnfbPullRequestEventListener.class); private static Invoker mockedInvoker = null; @VisibleForTesting public static void setInvoker(Invoker invoker) { PrnfbPullRequestEventListener.mockedInvoker = invoker; } private final ExecutorService executorService; private final PrnfbRendererFactory prnfbRendererFactory; private final PullRequestService pullRequestService; private final SettingsService settingsService; public PrnfbPullRequestEventListener( PrnfbRendererFactory prnfbRendererFactory, PullRequestService pullRequestService, ExecutorService executorService, SettingsService settingsService) { this.prnfbRendererFactory = prnfbRendererFactory; this.pullRequestService = pullRequestService; this.executorService = executorService; this.settingsService = settingsService; } private Invoker createInvoker() { if (mockedInvoker != null) { return mockedInvoker; } return new Invoker() { @Override public HttpResponse invoke(UrlInvoker urlInvoker) { return urlInvoker.invoke(); } }; } private void handleEvent(final PullRequestEvent pullRequestEvent) { if (pullRequestEvent.getPullRequest().isClosed() && pullRequestEvent instanceof PullRequestCommentEvent) { return; } final PrnfbSettingsData settings = settingsService.getPrnfbSettingsData(); ClientKeyStore clientKeyStore = new ClientKeyStore(settings); for (final PrnfbNotification notification : settingsService.getNotifications()) { PrnfbPullRequestAction action = fromPullRequestEvent(pullRequestEvent, notification); VariablesContext variables = new VariablesContextBuilder() // .setPullRequestEvent(pullRequestEvent) // .build(); PrnfbRenderer renderer = prnfbRendererFactory.create( pullRequestEvent.getPullRequest(), action, notification, variables, pullRequestEvent.getUser()); notify( notification, action, pullRequestEvent.getPullRequest(), renderer, clientKeyStore, settings.isShouldAcceptAnyCertificate()); } } @VisibleForTesting public void handleEventAsync(final PullRequestEvent pullRequestEvent) { executorService.execute( new Runnable() { @Override public void run() { handleEvent(pullRequestEvent); } }); } @VisibleForTesting boolean ignoreBecauseOfConflicting(TRIGGER_IF_MERGE triggerIfCanMerge, boolean isConflicted) { return triggerIfCanMerge == NOT_CONFLICTING && isConflicted || // triggerIfCanMerge == CONFLICTING && !isConflicted; } public boolean isNotificationTriggeredByAction( PrnfbNotification notification, PrnfbPullRequestAction pullRequestAction, PrnfbRenderer renderer, PullRequest pullRequest, ClientKeyStore clientKeyStore, Boolean shouldAcceptAnyCertificate) { if (!notification.getTriggers().contains(pullRequestAction)) { return FALSE; } if (notification.getProjectKey().isPresent()) { if (!notification .getProjectKey() .get() .equals(pullRequest.getToRef().getRepository().getProject().getKey())) { return FALSE; } } if (notification.getRepositorySlug().isPresent()) { if (!notification .getRepositorySlug() .get() .equals(pullRequest.getToRef().getRepository().getSlug())) { return FALSE; } } if (notification.getFilterRegexp().isPresent() && notification.getFilterString().isPresent() && !compile(notification.getFilterRegexp().get()) .matcher( renderer.render( notification.getFilterString().get(), ENCODE_FOR.NONE, clientKeyStore, shouldAcceptAnyCertificate)) .find()) { return FALSE; } if (notification.getTriggerIgnoreStateList().contains(pullRequest.getState())) { return FALSE; } if (notification.getTriggerIfCanMerge() != ALWAYS && pullRequest.isOpen()) { // Cannot perform canMerge unless PR is open boolean isConflicted = pullRequestService .canMerge(pullRequest.getToRef().getRepository().getId(), pullRequest.getId()) .isConflicted(); if (ignoreBecauseOfConflicting(notification.getTriggerIfCanMerge(), isConflicted)) { return FALSE; } } return TRUE; } public NotificationResponse notify( final PrnfbNotification notification, PrnfbPullRequestAction pullRequestAction, PullRequest pullRequest, PrnfbRenderer renderer, ClientKeyStore clientKeyStore, Boolean shouldAcceptAnyCertificate) { if (!isNotificationTriggeredByAction( notification, pullRequestAction, renderer, pullRequest, clientKeyStore, shouldAcceptAnyCertificate)) { return null; } Optional<String> postContent = absent(); if (notification.getPostContent().isPresent()) { ENCODE_FOR encodePostContentFor = notification.getPostContentEncoding(); postContent = of( renderer.render( notification.getPostContent().get(), encodePostContentFor, clientKeyStore, shouldAcceptAnyCertificate)); } String renderedUrl = renderer.render( notification.getUrl(), ENCODE_FOR.URL, clientKeyStore, shouldAcceptAnyCertificate); LOG.info( notification.getName() + " > " // + pullRequest.getFromRef().getId() + "(" + pullRequest.getFromRef().getLatestCommit() + ") -> " // + pullRequest.getToRef().getId() + "(" + pullRequest.getToRef().getLatestCommit() + ")" + " " // + renderedUrl); UrlInvoker urlInvoker = urlInvoker() // .withClientKeyStore(clientKeyStore) // .withUrlParam(renderedUrl) // .withMethod(notification.getMethod()) // .withPostContent(postContent) // .appendBasicAuth(notification); for (PrnfbHeader header : notification.getHeaders()) { urlInvoker // .withHeader( header.getName(), renderer.render( header.getValue(), ENCODE_FOR.NONE, clientKeyStore, shouldAcceptAnyCertificate)); } HttpResponse httpResponse = createInvoker() .invoke( urlInvoker // .withProxyServer(notification.getProxyServer()) // .withProxyPort(notification.getProxyPort()) // .withProxySchema(notification.getProxySchema()) // .withProxyUser(notification.getProxyUser()) // .withProxyPassword(notification.getProxyPassword()) // .shouldAcceptAnyCertificate(shouldAcceptAnyCertificate)); return new NotificationResponse(notification.getUuid(), notification.getName(), httpResponse); } @EventListener public void onEvent(PullRequestParticipantStatusUpdatedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestCommentAddedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestCommentDeletedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestCommentEditedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestCommentRepliedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestDeclinedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestMergedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestOpenedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestReopenedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(final PullRequestRescopedEvent e) { handleEventAsync(e); } @EventListener public void onEvent(PullRequestUpdatedEvent e) { handleEventAsync(e); } }