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

Commit

Permalink
Custom HTTP headers, proxy support, PULL_REQUEST_COMMENT_TEXT and PUL…
Browse files Browse the repository at this point in the history
…L_REQUEST_VERSION variables

Also:
* Marking plugin as compatible with Stash Data Center
* Replacing spaces in URL with %20
  • Loading branch information
tomasbjerre committed Apr 17, 2015
1 parent 067705e commit a213fbd
Show file tree
Hide file tree
Showing 21 changed files with 749 additions and 194 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Changelog of Pull Request Notifier for Stash.

## 1.11
* Custom HTTP headers
* Proxy support
* Stash Data Center compatibility
* Adding PULL_REQUEST_COMMENT_TEXT and PULL_REQUEST_VERSION variables

## 1.10
* Adding PULL_REQUEST_FROM_BRANCH and PULL_REQUEST_TO_BRANCH variables to make branch names available

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ The Pull Request Notifier for Stash can:
* Be configured to trigger on any pull request event. Including source branch change (RESCOPED_FROM) and target branch change (RESCOPED_TO).
* 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
* Can optionally use proxy to connect

The filter text as well as the URL support variables. These are:

* ${PULL_REQUEST_ID} Example: 1
* ${PULL_REQUEST_VERSION} Example: 1
* ${PULL_REQUEST_COMMENT_TEXT} Example: A comment
* ${PULL_REQUEST_ACTION} Example: OPENED
* ${PULL_REQUEST_AUTHOR_DISPLAY_NAME} Example: Administrator
* ${PULL_REQUEST_AUTHOR_EMAIL} Example: [email protected]
Expand Down
Binary file modified sandbox/all.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified sandbox/closer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/main/java/se/bjurr/prnfs/admin/AdminFormValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public final class AdminFormValues extends ArrayList<Map<String, String>> {
public static final String VALUE = "value";

public enum FIELDS {
user, password, events, FORM_IDENTIFIER, url, filter_string, filter_regexp, method, post_content
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
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package se.bjurr.prnfs.listener;

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Optional.absent;
import static java.util.regex.Pattern.compile;
import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.xml.bind.DatatypeConverter.printBase64Binary;
import static se.bjurr.prnfs.listener.PrnfsPullRequestAction.fromPullRequestEvent;
import static se.bjurr.prnfs.listener.UrlInvoker.urlInvoker;
import static se.bjurr.prnfs.settings.SettingsStorage.getPrnfsSettings;

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

import se.bjurr.prnfs.settings.Header;
import se.bjurr.prnfs.settings.PrnfsNotification;
import se.bjurr.prnfs.settings.PrnfsSettings;
import se.bjurr.prnfs.settings.ValidationException;
Expand All @@ -29,17 +34,27 @@

public class PrnfsPullRequestEventListener {

private UrlInvoker urlInvoker = new UrlInvoker();
public interface Invoker {
void invoke(UrlInvoker urlInvoker);
}

private final PluginSettingsFactory pluginSettingsFactory;
private static final Logger logger = LoggerFactory.getLogger(PrnfsPullRequestEventListener.class);

public PrnfsPullRequestEventListener(PluginSettingsFactory pluginSettingsFactory) {
this.pluginSettingsFactory = pluginSettingsFactory;
}
private static Invoker invoker = new Invoker() {
@Override
public void invoke(UrlInvoker urlInvoker) {
urlInvoker.invoke();
}
};

@VisibleForTesting
public void setUrlInvoker(UrlInvoker urlInvoker) {
this.urlInvoker = urlInvoker;
public static void setInvoker(Invoker invoker) {
PrnfsPullRequestEventListener.invoker = invoker;
}

public PrnfsPullRequestEventListener(PluginSettingsFactory pluginSettingsFactory) {
this.pluginSettingsFactory = pluginSettingsFactory;
}

@EventListener
Expand Down Expand Up @@ -102,7 +117,21 @@ public void handleEvent(PullRequestEvent o, PrnfsPullRequestAction action) {
if (n.getPostContent().isPresent()) {
postContent = Optional.of(renderer.render(n.getPostContent().get()));
}
urlInvoker.ivoke(renderer.render(n.getUrl()), n.getUser(), n.getPassword(), n.getMethod(), postContent);
UrlInvoker urlInvoker = urlInvoker().withUrlParam(renderer.render(n.getUrl())).withMethod(n.getMethod())
.withPostContent(postContent);
if (n.getUser().isPresent() && n.getPassword().isPresent()) {
final String userpass = n.getUser().get() + ":" + n.getPassword().get();
final String basicAuth = "Basic " + new String(printBase64Binary(userpass.getBytes(UTF_8)));
urlInvoker.withHeader(AUTHORIZATION, basicAuth);
}
for (Header header : n.getHeaders()) {
urlInvoker.withHeader(header.getName(), renderer.render(header.getValue()));
}
urlInvoker.withProxyServer(n.getProxyServer());
urlInvoker.withProxyPort(n.getProxyPort());
urlInvoker.withProxyUser(n.getProxyUser());
urlInvoker.withProxyPassword(n.getProxyPassword());
invoker.invoke(urlInvoker);
}
}
} catch (final ValidationException e) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/se/bjurr/prnfs/listener/PrnfsRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static se.bjurr.prnfs.listener.PrnfsPullRequestAction.fromPullRequestEvent;

import com.atlassian.stash.event.pull.PullRequestCommentAddedEvent;
import com.atlassian.stash.event.pull.PullRequestEvent;
import com.atlassian.stash.pull.PullRequestRef;

Expand Down Expand Up @@ -58,6 +59,11 @@ public String resolve(PullRequestEvent pullRequestEvent) {
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getId() + "";
}
}), PULL_REQUEST_VERSION(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getVersion() + "";
}
}), PULL_REQUEST_AUTHOR_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
Expand Down Expand Up @@ -123,6 +129,15 @@ public String resolve(PullRequestEvent pullRequestEvent) {
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getSlug() + "";
}
}), PULL_REQUEST_COMMENT_TEXT(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
if (pullRequestEvent instanceof PullRequestCommentAddedEvent) {
return ((PullRequestCommentAddedEvent) pullRequestEvent).getComment().getText();
} else {
return "";
}
}
});

private Resolver resolver;
Expand Down
151 changes: 131 additions & 20 deletions src/main/java/se/bjurr/prnfs/listener/UrlInvoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,98 @@

import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Joiner.on;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.io.CharStreams.readLines;
import static java.lang.Boolean.TRUE;
import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
import static javax.xml.bind.DatatypeConverter.printBase64Binary;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

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

import se.bjurr.prnfs.settings.Header;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.io.Closeables;

public class UrlInvoker {

private static final Logger logger = LoggerFactory.getLogger(UrlInvoker.class);
private String urlParam;
private String method;
private Optional<String> postContent;
private final List<Header> headers = newArrayList();
private Optional<String> proxyUser;
private Optional<String> proxyPassword;
private Optional<String> proxyHost;
private Integer proxyPort;

private UrlInvoker() {
}

public static UrlInvoker urlInvoker() {
return new UrlInvoker();
}

public UrlInvoker withHeader(String name, String value) {
headers.add(new Header(name, value));
return this;
}

public UrlInvoker withMethod(String method) {
this.method = method;
return this;
}

public UrlInvoker withPostContent(Optional<String> postContent) {
this.postContent = postContent;
return this;
}

public void ivoke(String urlParam, Optional<String> user, Optional<String> password, String method,
Optional<String> postContent) {
public UrlInvoker withUrlParam(String urlParam) {
this.urlParam = urlParam.replaceAll("\\s", "%20");
return this;
}

public void invoke() {
InputStreamReader ir = null;
DataOutputStream wr = null;
try {
logger.info("Url: \"" + urlParam + "\"");
final URL url = new URL(urlParam);
final HttpURLConnection uc = (HttpURLConnection) url.openConnection();
HttpURLConnection uc = null;
if (shouldUseProxy()) {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(getProxyHost().get(), getProxyPort()));
if (shouldAuthenticateProxy()) {
Authenticator authenticator = new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return (new PasswordAuthentication(getProxyUser().get(), getProxyPassword().get().toCharArray()));
}
};
Authenticator.setDefault(authenticator);
}
uc = (HttpURLConnection) url.openConnection(proxy);
} else {
uc = (HttpURLConnection) url.openConnection();
}
uc.setRequestMethod(method);
setAuthorization(uc, user, password);
for (Header header : headers) {
logger.info("header: \"" + header.getName() + "\" value: \"" + header.getValue() + "\"");
uc.setRequestProperty(header.getName(), getHeaderValue(header));
}
uc.setDoOutput(true);
if (shouldPostContent(method, postContent)) {
if (shouldPostContent()) {
logger.debug(method + " >\n" + postContent.get());
uc.setDoInput(true);
uc.setRequestProperty("Content-Length", postContent.get().length() + "");
Expand All @@ -58,24 +115,78 @@ public void ivoke(String urlParam, Optional<String> user, Optional<String> passw
}

@VisibleForTesting
void setAuthorization(URLConnection uc, Optional<String> user, Optional<String> password)
throws UnsupportedEncodingException {
if (shouldUseBasicAuth(user, password)) {
logger.info("user: \"" + user.or("") + "\" password: \"" + password.or("") + "\"");
final String userpass = user.get() + ":" + password.get();
final String basicAuth = "Basic " + new String(printBase64Binary(userpass.getBytes(UTF_8)));
uc.setRequestProperty(AUTHORIZATION, basicAuth);
}
public boolean shouldAuthenticateProxy() {
return getProxyUser().isPresent() && getProxyPassword().isPresent();
}

@VisibleForTesting
public static String getHeaderValue(Header header) {
return header.getValue();
}

public String getMethod() {
return method;
}

public Optional<String> getPostContent() {
return postContent;
}

public String getUrlParam() {
return urlParam;
}

public static Logger getLogger() {
return logger;
}

@VisibleForTesting
public static boolean shouldPostContent(String method, Optional<String> postContent) {
public boolean shouldPostContent() {
return (method.equals("POST") || method.equals("PUT")) && postContent.isPresent();
}

public List<Header> getHeaders() {
return headers;
}

@VisibleForTesting
public static boolean shouldUseBasicAuth(Optional<String> user, Optional<String> password) {
return user.isPresent() && password.isPresent();
public boolean shouldUseProxy() {
return getProxyHost().isPresent() && getProxyPort() > 0;
}

public Optional<String> getProxyUser() {
return proxyUser;
}

public UrlInvoker withProxyUser(Optional<String> proxyUser) {
this.proxyUser = proxyUser;
return this;
}

public Optional<String> getProxyPassword() {
return proxyPassword;
}

public UrlInvoker withProxyPassword(Optional<String> proxyPassword) {
this.proxyPassword = proxyPassword;
return this;
}

public Optional<String> getProxyHost() {
return proxyHost;
}

public UrlInvoker withProxyServer(Optional<String> proxyHost) {
this.proxyHost = proxyHost;
return this;
}

public Integer getProxyPort() {
return proxyPort;
}

public UrlInvoker withProxyPort(Integer proxyPort) {
this.proxyPort = proxyPort;
return this;
}
}
24 changes: 24 additions & 0 deletions src/main/java/se/bjurr/prnfs/settings/Header.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package se.bjurr.prnfs.settings;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.nullToEmpty;

public class Header {

private final String name;
private final String value;

public Header(String name, String value) {
this.name = checkNotNull(emptyToNull(nullToEmpty(name).trim()));
this.value = checkNotNull(emptyToNull(nullToEmpty(value).trim()));
}

public String getName() {
return name;
}

public String getValue() {
return value;
}
}
Loading

0 comments on commit a213fbd

Please sign in to comment.