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

Commit

Permalink
Adding support for PULL_REQUEST_FROM/TO_HASH, filters and fixing auth…
Browse files Browse the repository at this point in the history
…entication bug

* Making it compatible with Stash 2.12.0
* Some more documentation
* Validating regexp
* Log events
  • Loading branch information
tomasbjerre committed Mar 30, 2015
1 parent c6d4164 commit 60bea78
Show file tree
Hide file tree
Showing 19 changed files with 548 additions and 243 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Pull Request Notifier for Stash Changelog

Changelog of Pull Request Notifier for Stash
Changelog of Pull Request Notifier for Stash.

## 1.1

* Adding support for filters
* Adding support for PULL_REQUEST_TO_HASH and PULL_REQUEST_FROM_HASH variables
* Fixing authentication bug
* Making it compatible with Stash 2.12.0

## 1.0

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ http://server/job/theJob/buildWithParameters?token=TOKEN&PARAMETER=Value

[Here](https://raw.githubusercontent.com/tomasbjerre/pull-request-notifier-for-stash/master/sandbox/all.png) is a screenshot of the admin GUI.

Available in [Atlassian Marketplace](https://marketplace.atlassian.com/plugins/se.bjurr.prnfs.pull-request-notifier-for-stash).

## Features
The Pull Request Notifier for Stash can

* Invoke any URL when a pull request event happens.
* Invoke any URL, or set of URL:s, when a pull request event happens.
* With variables available to add necessary parameters.
* Be configured to trigger on any pull request event.
* Be configured to only trigger only if the pull request mathches a filter. Where a filter can target (among other things) branch, project or repo.
* Authenticate with HTTP BASIC authentication.

## Developer instructions
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
user, password, events, FORM_IDENTIFIER, url, filter_string, filter_regexp
}
}
2 changes: 1 addition & 1 deletion src/main/java/se/bjurr/prnfs/admin/ConfigResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@

@Path("/")
public class ConfigResource {
private static final Logger logger = LoggerFactory.getLogger(ConfigResource.class);
private final PluginSettingsFactory pluginSettingsFactory;
private final TransactionTemplate transactionTemplate;
private static final Logger logger = LoggerFactory.getLogger(ConfigResource.class);
private final UserManager userManager;

public ConfigResource(UserManager userManager, PluginSettingsFactory pluginSettingsFactory,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package se.bjurr.prnfs.listener;

import static com.google.common.cache.CacheBuilder.newBuilder;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.util.Collections.sort;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.regex.Pattern.compile;
import static se.bjurr.prnfs.settings.SettingsStorage.getPrnfsSettings;

import java.util.ArrayList;

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

import se.bjurr.prnfs.listener.PrnfsRenderer.PrnfsVariable;
import se.bjurr.prnfs.settings.PrnfsNotification;
import se.bjurr.prnfs.settings.PrnfsSettings;
import se.bjurr.prnfs.settings.ValidationException;
Expand Down Expand Up @@ -41,18 +47,34 @@ public void anEvent(PullRequestEvent o) {
if (dublicateEventBug(o)) {
return;
}
final PrnfsRenderer renderer = new PrnfsRenderer(o);
logEvent(renderer, o);
try {
final PrnfsSettings settings = getPrnfsSettings(pluginSettingsFactory.createGlobalSettings());
for (final PrnfsNotification n : settings.getNotifications()) {
if (n.getFilterRegexp().isPresent() && n.getFilterString().isPresent()
&& !compile(n.getFilterRegexp().get()).matcher(renderer.render(n.getFilterString().get())).find()) {
continue;
}
if (n.getTriggers().contains(o.getAction())) {
urlInvoker.ivoke(new PrnfsRenderer(o).render(n.getUrl()), n.getUser(), n.getPassword());
urlInvoker.ivoke(renderer.render(n.getUrl()), n.getUser(), n.getPassword());
}
}
} catch (final ValidationException e) {
logger.error("", e);
}
}

private void logEvent(PrnfsRenderer renderer, PullRequestEvent o) {
final StringBuilder renderString = new StringBuilder();
final ArrayList<PrnfsVariable> variables = newArrayList(PrnfsRenderer.PrnfsVariable.values());
sort(variables);
for (final PrnfsRenderer.PrnfsVariable variable : variables) {
renderString.append(" " + variable.name() + ": ${" + variable.name() + "}");
}
logger.info(renderer.render(renderString.toString().trim()));
}

/**
* Looks like there is a bug in Stash that causes events to be fired twice.
*/
Expand Down
68 changes: 39 additions & 29 deletions src/main/java/se/bjurr/prnfs/listener/PrnfsRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,81 @@

public class PrnfsRenderer {

public interface Resolver {
public String resolve(PullRequestEvent pullRequestEvent);
}

public enum PrnfsVariable {
PULL_REQUEST_ID(new Resolver() {
PULL_REQUEST_FROM_HASH(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getId() + "";
return pullRequestEvent.getPullRequest().getFromRef().getLatestChangeset();
}
}), PULL_REQUEST_FROM_HASH(new Resolver() {
}), PULL_REQUEST_FROM_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getLatestCommit();
return pullRequestEvent.getPullRequest().getFromRef().getId();
}
}), PULL_REQUEST_FROM_ID(new Resolver() {
}), PULL_REQUEST_FROM_REPO_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getId();
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getId() + "";
}
}), PULL_REQUEST_FROM_REPO_NAME(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getName() + "";
}
}), PULL_REQUEST_FROM_REPO_PROJECT_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getProject().getId() + "";
}
}), PULL_REQUEST_TO_REPO_PROJECT_ID(new Resolver() {
}), PULL_REQUEST_FROM_REPO_PROJECT_KEY(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getProject().getId() + "";
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getProject().getKey();
}
}), PULL_REQUEST_FROM_REPO_ID(new Resolver() {
}), PULL_REQUEST_FROM_REPO_SLUG(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getId() + "";
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getSlug() + "";
}
}), PULL_REQUEST_TO_REPO_ID(new Resolver() {
}), PULL_REQUEST_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getId() + "";
return pullRequestEvent.getPullRequest().getId() + "";
}
}), PULL_REQUEST_FROM_REPO_NAME(new Resolver() {
}), PULL_REQUEST_TO_HASH(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getName() + "";
return pullRequestEvent.getPullRequest().getToRef().getLatestChangeset();
}
}), PULL_REQUEST_TO_REPO_NAME(new Resolver() {
}), PULL_REQUEST_TO_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getName() + "";
return pullRequestEvent.getPullRequest().getToRef().getId();
}
}), PULL_REQUEST_FROM_REPO_SLUG(new Resolver() {
}), PULL_REQUEST_TO_REPO_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getSlug() + "";
return pullRequestEvent.getPullRequest().getToRef().getRepository().getId() + "";
}
}), PULL_REQUEST_TO_REPO_SLUG(new Resolver() {
}), PULL_REQUEST_TO_REPO_NAME(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getSlug() + "";
return pullRequestEvent.getPullRequest().getToRef().getRepository().getName() + "";
}
}), PULL_REQUEST_FROM_REPO_PROJECT_KEY(new Resolver() {
}), PULL_REQUEST_TO_REPO_PROJECT_ID(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getFromRef().getRepository().getProject().getKey();
return pullRequestEvent.getPullRequest().getToRef().getRepository().getProject().getId() + "";
}
}), PULL_REQUEST_TO_REPO_PROJECT_KEY(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getRepository().getProject().getKey();
}
}), PULL_REQUEST_TO_ID(new Resolver() {
}), PULL_REQUEST_TO_REPO_SLUG(new Resolver() {
@Override
public String resolve(PullRequestEvent pullRequestEvent) {
return pullRequestEvent.getPullRequest().getToRef().getId();
return pullRequestEvent.getPullRequest().getToRef().getRepository().getSlug() + "";
}
});

Expand All @@ -92,6 +93,10 @@ public String resolve(PullRequestEvent pullRequestEvent) {
}
}

public interface Resolver {
public String resolve(PullRequestEvent pullRequestEvent);
}

private final PullRequestEvent pullRequestEvent;

public PrnfsRenderer(PullRequestEvent pullRequestEvent) {
Expand All @@ -102,7 +107,12 @@ public String render(String string) {
for (final PrnfsVariable variable : PrnfsVariable.values()) {
final String regExpStr = "\\$\\{" + variable.name() + "\\}";
if (string.contains(regExpStr.replaceAll("\\\\", ""))) {
string = string.replaceAll(regExpStr, variable.resolve(pullRequestEvent));
try {
string = string.replaceAll(regExpStr, variable.resolve(pullRequestEvent));
} catch (final NullPointerException e) {
// So that all values does not need to be set for all test cases
return string;
}
}
}
return string;
Expand Down
35 changes: 31 additions & 4 deletions src/main/java/se/bjurr/prnfs/settings/PrnfsNotification.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,60 @@
import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.nullToEmpty;
import static java.util.regex.Pattern.compile;

import java.net.URL;
import java.util.List;

import se.bjurr.prnfs.admin.AdminFormValues;

import com.atlassian.stash.pull.PullRequestAction;
import com.google.common.base.Optional;

public class PrnfsNotification {
private final String filterRegexp;
private final String filterString;
private final String password;
private final List<PullRequestAction> triggers;
private final String url;
private final String user;

public PrnfsNotification(List<PullRequestAction> triggers, String password, String url, String user)
throws ValidationException {
public PrnfsNotification(List<PullRequestAction> triggers, String url, String user, String password,
String filterString, String filterRegexp) throws ValidationException {
this.password = nullToEmpty(password).trim();
if (nullToEmpty(url).trim().isEmpty()) {
throw new ValidationException("url", "URL not set!");
throw new ValidationException(AdminFormValues.FIELDS.url.name(), "URL not set!");
}
try {
new URL(url);
} catch (final Exception e) {
throw new ValidationException("url", "URL not valid!");
throw new ValidationException(AdminFormValues.FIELDS.url.name(), "URL not valid!");
}
if (!nullToEmpty(filterRegexp).trim().isEmpty()) {
try {
compile(filterRegexp);
} catch (final Exception e) {
throw new ValidationException(AdminFormValues.FIELDS.filter_regexp.name(), "Filter regexp not valid! "
+ e.getMessage().replaceAll("\n", " "));
}
if (nullToEmpty(filterString).trim().isEmpty()) {
throw new ValidationException(AdminFormValues.FIELDS.filter_string.name(),
"Filter string not set, nothing to match regexp against!");
}
}
this.url = url;
this.user = nullToEmpty(user).trim();
this.triggers = checkNotNull(triggers);
this.filterString = filterString;
this.filterRegexp = filterRegexp;
}

public Optional<String> getFilterRegexp() {
return fromNullable(filterRegexp);
}

public Optional<String> getFilterString() {
return fromNullable(filterString);
}

public Optional<String> getPassword() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,31 @@ public static PrnfsNotificationBuilder prnfsNotificationBuilder() {
private final List<PullRequestAction> triggers = newArrayList();
private String url;
private String user;
private String filterRegexp;
private String filterString;

private PrnfsNotificationBuilder() {
}

public PrnfsNotification build() throws ValidationException {
return new PrnfsNotification(triggers, password, url, user);
return new PrnfsNotification(triggers, url, user, password, filterString, filterRegexp);
}

public PrnfsNotificationBuilder withPassword(String password) {
this.password = checkNotNull(password);
return this;
}

public PrnfsNotificationBuilder withFilterRegexp(String filterRegexp) {
this.filterRegexp = checkNotNull(filterRegexp);
return this;
}

public PrnfsNotificationBuilder withFilterString(String filterString) {
this.filterString = checkNotNull(filterString);
return this;
}

public PrnfsNotificationBuilder withTrigger(PullRequestAction trigger) {
this.triggers.add(checkNotNull(trigger));
return this;
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/se/bjurr/prnfs/settings/SettingsStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ public class SettingsStorage {
private static final Gson gson = new Gson();
private static final Logger logger = LoggerFactory.getLogger(SettingsStorage.class);

private static final String STORAGE_KEY = AdminFormValues.class.getName() + "_2";

private static Random random = new Random(currentTimeMillis());

private static final String STORAGE_KEY = AdminFormValues.class.getName() + "_2";

public static void deleteSettings(PluginSettings pluginSettings, String id) {
final Map<String, AdminFormValues> map = getNotificationsMap(pluginSettings);
map.remove(id);
Expand Down Expand Up @@ -94,6 +94,20 @@ public static PrnfsNotification getPrnfsNotification(AdminFormValues a) throws V
for (final Map<String, String> event : events) {
prnfsNotificationBuilder.withTrigger(PullRequestAction.valueOf(event.get(VALUE)));
}
if (tryFind(a, predicate(AdminFormValues.FIELDS.user.name())).isPresent()) {
prnfsNotificationBuilder.withUser(find(a, predicate(AdminFormValues.FIELDS.user.name())).get(VALUE));
}
if (tryFind(a, predicate(AdminFormValues.FIELDS.password.name())).isPresent()) {
prnfsNotificationBuilder.withPassword(find(a, predicate(AdminFormValues.FIELDS.password.name())).get(VALUE));
}
if (tryFind(a, predicate(AdminFormValues.FIELDS.filter_string.name())).isPresent()) {
prnfsNotificationBuilder
.withFilterString(find(a, predicate(AdminFormValues.FIELDS.filter_string.name())).get(VALUE));
}
if (tryFind(a, predicate(AdminFormValues.FIELDS.filter_regexp.name())).isPresent()) {
prnfsNotificationBuilder
.withFilterRegexp(find(a, predicate(AdminFormValues.FIELDS.filter_regexp.name())).get(VALUE));
}
return prnfsNotificationBuilder.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package se.bjurr.prnfs.settings;


public class ValidationException extends Exception {
private static final long serialVersionUID = 2203598567281456784L;
private final String error;
Expand Down
6 changes: 2 additions & 4 deletions src/main/resources/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@
data: JSON.stringify($form.serializeArray(), null, 2),
processData: false,
error: function(xhr, data, error) {
$.each(xhr.responseJSON, function(field,errorString) {
$(".error."+field,$form).html(errorString);
$(".post",$form).html("There were errors, form not saved!");
});
$(".error."+xhr.responseJSON.field,$form).html(xhr.responseJSON.error);
$(".post",$form).html("There were errors, form not saved!");
},
success: function(data, text, xhr) {
getAll();
Expand Down
Loading

0 comments on commit 60bea78

Please sign in to comment.