Skip to content
This repository has been archived by the owner on Jun 9, 2021. It is now read-only.

Commit

Permalink
Trigger Notification Button on Pull Request View #33
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasbjerre committed Aug 8, 2015
1 parent da1c1dd commit 98869f8
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 175 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog of Pull Request Notifier for Stash.

## 1.18
* Triggers can be named. To make it easier to keep track of them in large installations.
* Trigger Notification Button on Pull Request View
* Building against latest Stash version (3.11.1) using latest Atlassian Maven Plugin Suite version (6.0.3)

## 1.17
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ The Pull Request Notifier for Stash can:
* Invoke any URL, or set of URL:s, when a pull request event happens.
* With variables available to add necessary parameters.
* HTTP POST, PUT, GET and DELETE. POST and PUT also supports rendered post content.
* Be configured to trigger on any [pull request event](https://developer.atlassian.com/static/javadoc/stash/3.10.0/api/reference/com/atlassian/stash/event/pull/package-summary.html). Including source branch change (RESCOPED_FROM) and target branch change (RESCOPED_TO).
* Be configured to trigger on any [pull request event](https://developer.atlassian.com/static/javadoc/stash/3.10.0/api/reference/com/atlassian/stash/event/pull/package-summary.html). Including extended events:
* RESCOPED_FROM, when source branch change
* RESCOPED_TO, when target branch change
* MANUAL_TRIGGER, when trigger button in pull request view is pressed
* Be configured to only trigger if the pull request mathches a filter. A filter text is constructed with any combination of the variables and then a regexp is constructed to match that text.
* Authenticate with HTTP basic authentication.
* Send custom HTTP headers
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@
<artifactId>stash-scm-git-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugins</groupId>
<artifactId>atlassian-plugins-webfragment</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
Expand Down
89 changes: 89 additions & 0 deletions src/main/java/se/bjurr/prnfs/ManualResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package se.bjurr.prnfs;

import static com.atlassian.stash.user.Permission.ADMIN;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
import static se.bjurr.prnfs.listener.PrnfsPullRequestAction.MANUAL_TRIGGER;
import static se.bjurr.prnfs.settings.SettingsStorage.getPrnfsSettings;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import se.bjurr.prnfs.listener.PrnfsPullRequestAction;
import se.bjurr.prnfs.listener.PrnfsPullRequestEventListener;
import se.bjurr.prnfs.listener.PrnfsRenderer;
import se.bjurr.prnfs.settings.PrnfsNotification;
import se.bjurr.prnfs.settings.PrnfsSettings;

import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.stash.event.pull.PullRequestEvent;
import com.atlassian.stash.pull.PullRequest;
import com.atlassian.stash.pull.PullRequestService;
import com.atlassian.stash.repository.RepositoryService;
import com.atlassian.stash.user.Permission;
import com.atlassian.stash.user.SecurityService;
import com.atlassian.stash.user.StashUser;
import com.atlassian.stash.user.UserService;
import com.atlassian.stash.util.Operation;

@Path("/manual")
public class ManualResource {
private final UserManager userManager;
private final UserService userService;
private final PullRequestService pullRequestService;
private final PrnfsPullRequestEventListener prnfsPullRequestEventListener;
private final RepositoryService repositoryService;
private final SecurityService securityService;
private final PluginSettingsFactory pluginSettingsFactory;

public ManualResource(UserManager userManager, UserService userService, PluginSettingsFactory pluginSettingsFactory,
PullRequestService pullRequestService, PrnfsPullRequestEventListener prnfsPullRequestEventListener,
RepositoryService repositoryService, SecurityService securityService) {
this.userManager = userManager;
this.userService = userService;
this.pullRequestService = pullRequestService;
this.prnfsPullRequestEventListener = prnfsPullRequestEventListener;
this.repositoryService = repositoryService;
this.securityService = securityService;
this.pluginSettingsFactory = pluginSettingsFactory;
}

@GET
@Path("{repositoryId}/{pullRequestId}")
@Produces(APPLICATION_JSON)
public Response get(@Context HttpServletRequest request, @PathParam("repositoryId") Integer repositoryId,
@PathParam("pullRequestId") Long pullRequestId) throws Exception {
if (userManager.getRemoteUser(request) == null) {
return status(UNAUTHORIZED).build();
}

final PullRequest pullRequest = pullRequestService.getById(repositoryId, pullRequestId);
PullRequestEvent pullRequestEvent = null;
final PrnfsSettings settings = securityService.withPermission(ADMIN, "Getting config").call(
new Operation<PrnfsSettings, Exception>() {
@Override
public PrnfsSettings perform() throws Exception {
return getPrnfsSettings(pluginSettingsFactory.createGlobalSettings());
}
});
for (PrnfsNotification prnfsNotification : settings.getNotifications()) {
PrnfsPullRequestAction pullRequestAction = PrnfsPullRequestAction.valueOf(MANUAL_TRIGGER);
if (!prnfsNotification.getTriggers().contains(pullRequestAction)) {
continue;
}
StashUser stashUser = userService.getUserBySlug(userManager.getRemoteUser(request).getUsername());
PrnfsRenderer renderer = new PrnfsRenderer(pullRequest, pullRequestAction, stashUser, repositoryService,
prnfsNotification, pullRequestEvent);
prnfsPullRequestEventListener.notify(prnfsNotification, renderer, pullRequest);
}
return status(OK).build();
}
}
69 changes: 69 additions & 0 deletions src/main/java/se/bjurr/prnfs/ShouldShowTriggerButtonCondition.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package se.bjurr.prnfs;

import static com.atlassian.stash.user.Permission.ADMIN;
import static java.lang.Boolean.TRUE;
import static se.bjurr.prnfs.settings.SettingsStorage.getPrnfsSettings;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import se.bjurr.prnfs.settings.PrnfsSettings;

import com.atlassian.plugin.PluginParseException;
import com.atlassian.plugin.web.Condition;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.stash.user.SecurityService;
import com.atlassian.stash.user.StashAuthenticationContext;
import com.atlassian.stash.util.Operation;

public class ShouldShowTriggerButtonCondition implements Condition {
private static final Logger logger = LoggerFactory.getLogger(ShouldShowTriggerButtonCondition.class);

public static final String REPOSITORY = "repository";
private final PluginSettingsFactory pluginSettingsFactory;
private final SecurityService securityService;
private final UserManager userManager;
private final StashAuthenticationContext stashAuthenticationContext;

public ShouldShowTriggerButtonCondition(PluginSettingsFactory pluginSettingsFactory, SecurityService securityService,
StashAuthenticationContext stashAuthenticationContext, UserManager userManager) {
this.pluginSettingsFactory = pluginSettingsFactory;
this.securityService = securityService;
this.userManager = userManager;
this.stashAuthenticationContext = stashAuthenticationContext;
}

@Override
public void init(Map<String, String> map) throws PluginParseException {

}

@Override
public boolean shouldDisplay(Map<String, Object> context) {
try {
PrnfsSettings settings = securityService.withPermission(ADMIN, "Getting config").call(
new Operation<PrnfsSettings, Exception>() {
@Override
public PrnfsSettings perform() throws Exception {
return getPrnfsSettings(pluginSettingsFactory.createGlobalSettings());
}
});

return shouldShowTriggerButton(settings);
} catch (Exception e) {
logger.error("Error when checking if PRNFS trigger button should be shown in PR view", e);
return false;
}
}

private boolean shouldShowTriggerButton(PrnfsSettings settings) {
// TODO
stashAuthenticationContext.getCurrentUser();
userManager.isAdmin(userManager.getRemoteUserKey());
return TRUE;
}

}
4 changes: 3 additions & 1 deletion src/main/java/se/bjurr/prnfs/admin/AdminFormValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ public final class AdminFormValues extends ArrayList<Map<String, String>> {

public static final String DEFAULT_NAME = "Unnamed trigger";

public static final String GLOBAL_CONFIG_FORM_IDENTIFIER = "GLOBAL_CONFIG_FORM_IDENTIFIER";

public enum FIELDS {
user, password, events, FORM_IDENTIFIER, url, filter_string, filter_regexp, method, post_content, proxy_user, proxy_password, proxy_server, proxy_port, header_name, header_value, name
user, password, events, FORM_IDENTIFIER, url, filter_string, filter_regexp, method, post_content, proxy_user, proxy_password, proxy_server, proxy_port, header_name, header_value, name, button_title, button_visibility
}
}
8 changes: 4 additions & 4 deletions src/main/java/se/bjurr/prnfs/admin/ConfigResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ConfigResource(UserManager userManager, PluginSettingsFactory pluginSetti
@DELETE
@Path("{id}")
public Response delete(@PathParam("id") final String id, @Context HttpServletRequest request) {
if (!isAdminLoggedIn(request)) {
if (!isSystemAdminLoggedIn(request)) {
return status(UNAUTHORIZED).build();
}

Expand All @@ -70,7 +70,7 @@ public Object doInTransaction() {
@GET
@Produces(APPLICATION_JSON)
public Response get(@Context HttpServletRequest request) {
if (!isAdminLoggedIn(request)) {
if (!isSystemAdminLoggedIn(request)) {
return status(UNAUTHORIZED).build();
}

Expand All @@ -94,7 +94,7 @@ public UserManager getUserManager() {
return userManager;
}

private boolean isAdminLoggedIn(HttpServletRequest request) {
private boolean isSystemAdminLoggedIn(HttpServletRequest request) {
final UserProfile user = userManager.getRemoteUser(request);
if (user == null) {
return false;
Expand All @@ -109,7 +109,7 @@ private boolean isAdminLoggedIn(HttpServletRequest request) {
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public Response post(final AdminFormValues config, @Context HttpServletRequest request) {
if (!isAdminLoggedIn(request)) {
if (!isSystemAdminLoggedIn(request)) {
return status(UNAUTHORIZED).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class PrnfsPullRequestAction {

public static final String RESCOPED_FROM = "RESCOPED_FROM";

public static final String MANUAL_TRIGGER = "MANUAL_TRIGGER";

private static final Map<String, PrnfsPullRequestAction> values = new ImmutableMap.Builder<String, PrnfsPullRequestAction>()
.put(APPROVED.name(), new PrnfsPullRequestAction(APPROVED.name())) //
.put(COMMENTED.name(), new PrnfsPullRequestAction(COMMENTED.name())) //
Expand All @@ -38,6 +40,7 @@ public class PrnfsPullRequestAction {
.put(RESCOPED_TO, new PrnfsPullRequestAction(RESCOPED_TO)) //
.put(UNAPPROVED.name(), new PrnfsPullRequestAction(UNAPPROVED.name())) //
.put(UPDATED.name(), new PrnfsPullRequestAction(UPDATED.name())) //
.put(MANUAL_TRIGGER, new PrnfsPullRequestAction(MANUAL_TRIGGER)) //
.build();

private final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ public void handleEvent(PullRequestEvent pullRequestEvent) {
}
final PrnfsSettings settings = getPrnfsSettings(pluginSettingsFactory.createGlobalSettings());
for (final PrnfsNotification notification : settings.getNotifications()) {
final PrnfsRenderer renderer = new PrnfsRenderer(pullRequestEvent, repositoryService, notification);
PrnfsPullRequestAction action = fromPullRequestEvent(pullRequestEvent, notification);
final PrnfsRenderer renderer = new PrnfsRenderer(pullRequestEvent.getPullRequest(), action,
pullRequestEvent.getUser(), repositoryService, notification, pullRequestEvent);
PullRequest pr = pullRequestEvent.getPullRequest();
if (notification.getFilterRegexp().isPresent()
&& notification.getFilterString().isPresent()
Expand All @@ -130,34 +131,38 @@ public void handleEvent(PullRequestEvent pullRequestEvent) {
continue;
}
if (notification.getTriggers().contains(action)) {
Optional<String> postContent = absent();
if (notification.getPostContent().isPresent()) {
postContent = Optional.of(renderer.render(notification.getPostContent().get()));
}
String renderedUrl = renderer.render(notification.getUrl());
logger.info(notification.getName() + " > " + action.getName() + " "//
+ pr.getFromRef().getId() + "(" + pr.getFromRef().getLatestChangeset() + ") -> " //
+ pr.getToRef().getId() + "(" + pr.getToRef().getLatestChangeset() + ")" + " " //
+ renderedUrl);
UrlInvoker urlInvoker = urlInvoker().withUrlParam(renderedUrl).withMethod(notification.getMethod())
.withPostContent(postContent);
if (notification.getUser().isPresent() && notification.getPassword().isPresent()) {
final String userpass = notification.getUser().get() + ":" + notification.getPassword().get();
final String basicAuth = "Basic " + new String(printBase64Binary(userpass.getBytes(UTF_8)));
urlInvoker.withHeader(AUTHORIZATION, basicAuth);
}
for (Header header : notification.getHeaders()) {
urlInvoker.withHeader(header.getName(), renderer.render(header.getValue()));
}
urlInvoker.withProxyServer(notification.getProxyServer());
urlInvoker.withProxyPort(notification.getProxyPort());
urlInvoker.withProxyUser(notification.getProxyUser());
urlInvoker.withProxyPassword(notification.getProxyPassword());
invoker.invoke(urlInvoker);
notify(notification, renderer, pr);
}
}
} catch (final ValidationException e) {
logger.error("", e);
}
}

public void notify(final PrnfsNotification notification, final PrnfsRenderer renderer, PullRequest pr) {
Optional<String> postContent = absent();
if (notification.getPostContent().isPresent()) {
postContent = Optional.of(renderer.render(notification.getPostContent().get()));
}
String renderedUrl = renderer.render(notification.getUrl());
logger.info(notification.getName() + " > " //
+ pr.getFromRef().getId() + "(" + pr.getFromRef().getLatestChangeset() + ") -> " //
+ pr.getToRef().getId() + "(" + pr.getToRef().getLatestChangeset() + ")" + " " //
+ renderedUrl);
UrlInvoker urlInvoker = urlInvoker().withUrlParam(renderedUrl).withMethod(notification.getMethod())
.withPostContent(postContent);
if (notification.getUser().isPresent() && notification.getPassword().isPresent()) {
final String userpass = notification.getUser().get() + ":" + notification.getPassword().get();
final String basicAuth = "Basic " + new String(printBase64Binary(userpass.getBytes(UTF_8)));
urlInvoker.withHeader(AUTHORIZATION, basicAuth);
}
for (Header header : notification.getHeaders()) {
urlInvoker.withHeader(header.getName(), renderer.render(header.getValue()));
}
urlInvoker.withProxyServer(notification.getProxyServer());
urlInvoker.withProxyPort(notification.getProxyPort());
urlInvoker.withProxyUser(notification.getProxyUser());
urlInvoker.withProxyPassword(notification.getProxyPassword());
invoker.invoke(urlInvoker);
}
}
Loading

0 comments on commit 98869f8

Please sign in to comment.