diff --git a/README.md b/README.md index 5fc78fe..ad5ca1c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ gocd.slack { slackDisplayName = "gocd-slack-bot" slackUserIconURL = "http://example.com/slack-bot.png" displayMaterialChanges = true + process-all-rules = true proxy { hostname = "localhost" port = "5555" @@ -40,6 +41,7 @@ gocd.slack { - `webhookUrl` - Slack Webhook URL - `channel` - Override the default channel where we should send the notifications in slack. You can also give a value starting with `@` to send it to any specific user. - `displayMaterialChanges` - Display material changes in the notification (git revisions for example). Defaults to true, set to false if you want to hide. +- `process-all-rules` - If true, all matching rules are applied instead of just the first. - `proxy` - Specify proxy related settings for the plugin. - `proxy.hostname` - Proxy Host - `proxy.port` - Proxy Port @@ -67,7 +69,7 @@ gocd.slack { }] } ``` -`gocd.slack.pipelines` contains all the rules for the go-server. It is a list of rules (see below for what the parameters mean) for various pipelines. The plugin will pick the first matching pipeline rule from the pipelines collection above, so your most specific rule should be first, with the most generic rule at the bottom. +`gocd.slack.pipelines` contains all the rules for the go-server. It is a list of rules (see below for what the parameters mean) for various pipelines. The plugin will pick the first matching pipeline rule from the pipelines collection above, so your most specific rule should be first, with the most generic rule at the bottom. Alternatively, set the `process-all-rules` option to `true` and all matching rules will be applied. - `name` - Regex to match the pipeline name - `stage` - Regex to match the stage name - `state` - State of the pipeline at which we should send a notification. You can provide multiple values separated by pipe (`|`) symbol. Valid values are passed, failed, cancelled, building, fixed, broken or all. diff --git a/docs/index.md b/docs/index.md index 5fc78fe..a79bcde 100644 --- a/docs/index.md +++ b/docs/index.md @@ -26,6 +26,7 @@ gocd.slack { slackDisplayName = "gocd-slack-bot" slackUserIconURL = "http://example.com/slack-bot.png" displayMaterialChanges = true + process-all-rules = true proxy { hostname = "localhost" port = "5555" @@ -40,6 +41,7 @@ gocd.slack { - `webhookUrl` - Slack Webhook URL - `channel` - Override the default channel where we should send the notifications in slack. You can also give a value starting with `@` to send it to any specific user. - `displayMaterialChanges` - Display material changes in the notification (git revisions for example). Defaults to true, set to false if you want to hide. +- `process-all-rules` - If true, all matching rules are applied instead of just the first. - `proxy` - Specify proxy related settings for the plugin. - `proxy.hostname` - Proxy Host - `proxy.port` - Proxy Port @@ -67,7 +69,7 @@ gocd.slack { }] } ``` -`gocd.slack.pipelines` contains all the rules for the go-server. It is a list of rules (see below for what the parameters mean) for various pipelines. The plugin will pick the first matching pipeline rule from the pipelines collection above, so your most specific rule should be first, with the most generic rule at the bottom. +`gocd.slack.pipelines` contains all the rules for the go-server. It is a list of rules (see below for what the parameters mean) for various pipelines. The plugin will pick the first matching pipeline rule from the pipelines collection above, so your most specific rule should be first, with the most generic rule at the bottom. Alternatively, set the `process-all-rules` option to `true` and all matching rules will be applied. - `name` - Regex to match the pipeline name - `stage` - Regex to match the stage name - `state` - State of the pipeline at which we should send a notification. You can provide multiple values separated by pipe (`|`) symbol. Valid values are passed, failed, cancelled, building, fixed, broken or all. diff --git a/src/main/java/in/ashwanthkumar/gocd/slack/PipelineListener.java b/src/main/java/in/ashwanthkumar/gocd/slack/PipelineListener.java index e37fb9e..bbe613c 100644 --- a/src/main/java/in/ashwanthkumar/gocd/slack/PipelineListener.java +++ b/src/main/java/in/ashwanthkumar/gocd/slack/PipelineListener.java @@ -6,6 +6,8 @@ import in.ashwanthkumar.gocd.slack.ruleset.Rules; import in.ashwanthkumar.utils.lang.option.Option; +import java.util.List; + abstract public class PipelineListener { private Logger LOG = Logger.getLoggerFor(PipelineListener.class); protected Rules rules; @@ -17,11 +19,15 @@ public PipelineListener(Rules rules) { public void notify(GoNotificationMessage message) throws Exception { message.tryToFixStageResult(rules); LOG.debug(String.format("Finding rules with state %s", message.getStageResult())); - Option ruleOption = rules.find(message.getPipelineName(), message.getStageName(), message.getStageResult()); - if (ruleOption.isDefined()) { - PipelineRule pipelineRule = ruleOption.get(); - LOG.debug(String.format("Matching rule is %s", pipelineRule)); - handlePipelineStatus(pipelineRule, PipelineStatus.valueOf(message.getStageResult().toUpperCase()), message); + List foundRules = rules.find(message.getPipelineName(), message.getStageName(), message.getStageResult()); + if (foundRules.size() > 0) { + for (PipelineRule pipelineRule : foundRules) { + LOG.debug(String.format("Matching rule is %s", pipelineRule)); + handlePipelineStatus(pipelineRule, PipelineStatus.valueOf(message.getStageResult().toUpperCase()), message); + if (! rules.getProcessAllRules()) { + break; + } + } } else { LOG.warn(String.format("Couldn't find any matching rule for %s/%s with status=%s", message.getPipelineName(), message.getStageName(), message.getStageResult())); } diff --git a/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java b/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java index 023e5cb..080dc03 100644 --- a/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java +++ b/src/main/java/in/ashwanthkumar/gocd/slack/ruleset/Rules.java @@ -30,6 +30,7 @@ public class Rules { private String goLogin; private String goPassword; private boolean displayMaterialChanges; + private boolean processAllRules; private Proxy proxy; @@ -139,6 +140,15 @@ public Rules setDisplayMaterialChanges(boolean displayMaterialChanges) { return this; } + public boolean getProcessAllRules() { + return processAllRules; + } + + public Rules setProcessAllRules(boolean processAllRules) { + this.processAllRules = processAllRules; + return this; + } + public Proxy getProxy() { return proxy; } @@ -152,12 +162,23 @@ public PipelineListener getPipelineListener() { return pipelineListener; } - public Option find(final String pipeline, final String stage, final String pipelineStatus) { - return Lists.find(pipelineRules, new Predicate() { + public List find(final String pipeline, final String stage, final String pipelineStatus) { + Predicate predicate = new Predicate() { public Boolean apply(PipelineRule input) { return input.matches(pipeline, stage, pipelineStatus); } - }); + }; + + if(processAllRules) { + return Lists.filter(pipelineRules, predicate); + } else { + List found = new ArrayList(); + Option match = Lists.find(pipelineRules, predicate); + if(match.isDefined()) { + found.add(match.get()); + } + return found; + } } public static Rules fromConfig(Config config) { @@ -199,6 +220,11 @@ public static Rules fromConfig(Config config) { displayMaterialChanges = config.getBoolean("displayMaterialChanges"); } + boolean processAllRules = false; + if (config.hasPath("process-all-rules")) { + processAllRules = config.getBoolean("process-all-rules"); + } + Proxy proxy = null; if (config.hasPath("proxy")) { Config proxyConfig = config.getConfig("proxy"); @@ -231,6 +257,7 @@ public PipelineRule apply(Config input) { .setGoLogin(login) .setGoPassword(password) .setDisplayMaterialChanges(displayMaterialChanges) + .setProcessAllRules(processAllRules) .setProxy(proxy); try { rules.pipelineListener = Class.forName(config.getString("listener")).asSubclass(PipelineListener.class).getConstructor(Rules.class).newInstance(rules); diff --git a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java index 20daa95..576e080 100644 --- a/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java +++ b/src/test/java/in/ashwanthkumar/gocd/slack/ruleset/RulesTest.java @@ -1,11 +1,11 @@ package in.ashwanthkumar.gocd.slack.ruleset; - import in.ashwanthkumar.gocd.slack.Status; import in.ashwanthkumar.utils.lang.option.Option; import org.junit.Test; import java.util.Arrays; +import java.util.List; import java.util.HashSet; import java.util.Set; @@ -17,8 +17,6 @@ public class RulesTest { @Test public void shouldFindMatch() { Rules rules = new Rules(); - PipelineRule pRules1 = new PipelineRule("pipeline", "stage"); - pRules1.setStatus(new HashSet(Arrays.asList(PipelineStatus.PASSED, PipelineStatus.FAILED))); rules.setPipelineRules(Arrays.asList( pipelineRule("pipeline1", "stage1", "ch1", statuses(PipelineStatus.BUILDING, PipelineStatus.FAILED)), @@ -26,25 +24,23 @@ public void shouldFindMatch() { pipelineRule("pipeline2", "stage2", "ch3", statuses(PipelineStatus.CANCELLED, PipelineStatus.BROKEN)) )); - Option rule1 = rules.find("pipeline1", "stage1", Status.Building.getStatus()); - assertThat(rule1.isDefined(), is(true)); - assertThat(rule1.get().getNameRegex(), is("pipeline1")); - assertThat(rule1.get().getStageRegex(), is("stage1")); + List foundRules1 = rules.find("pipeline1", "stage1", Status.Building.getStatus()); + assertThat(foundRules1.size(), is(1)); + assertThat(foundRules1.get(0).getNameRegex(), is("pipeline1")); + assertThat(foundRules1.get(0).getStageRegex(), is("stage1")); - Option rule2 = rules.find("pipeline2", "stage2", Status.Cancelled.getStatus()); - assertThat(rule2.isDefined(), is(true)); - assertThat(rule2.get().getNameRegex(), is("pipeline2")); - assertThat(rule2.get().getStageRegex(), is("stage2")); + List foundRules2 = rules.find("pipeline2", "stage2", Status.Cancelled.getStatus()); + assertThat(foundRules2.size(), is(1)); + assertThat(foundRules2.get(0).getNameRegex(), is("pipeline2")); + assertThat(foundRules2.get(0).getStageRegex(), is("stage2")); - Option rule3 = rules.find("pipeline2", "stage2", Status.Passed.getStatus()); - assertThat(rule3.isDefined(), is(false)); + List foundRules3 = rules.find("pipeline2", "stage2", Status.Passed.getStatus()); + assertThat(foundRules3.size(), is(0)); } @Test public void shouldFindMatchWithRegexp() { Rules rules = new Rules(); - PipelineRule pRules1 = new PipelineRule("pipeline", "stage"); - pRules1.setStatus(new HashSet(Arrays.asList(PipelineStatus.PASSED, PipelineStatus.FAILED))); rules.setPipelineRules(Arrays.asList( pipelineRule("[a-z]*", "[a-z]*", "ch1", statuses(PipelineStatus.BUILDING)), @@ -53,46 +49,66 @@ public void shouldFindMatchWithRegexp() { pipelineRule("\\d*", "[a-z]*", "ch4", statuses(PipelineStatus.BUILDING)) )); - Option rule1 = rules.find("abc", "efg", Status.Building.getStatus()); - assertThat(rule1.isDefined(), is(true)); - assertThat(rule1.get().getNameRegex(), is("[a-z]*")); - assertThat(rule1.get().getStageRegex(), is("[a-z]*")); - assertThat(rule1.get().getChannel(), is("ch1")); - - Option rule2 = rules.find("123", "456", Status.Building.getStatus()); - assertThat(rule2.isDefined(), is(true)); - assertThat(rule2.get().getNameRegex(), is("\\d*")); - assertThat(rule2.get().getStageRegex(), is("\\d*")); - assertThat(rule2.get().getChannel(), is("ch2")); - - Option rule3 = rules.find("123", "456", Status.Passed.getStatus()); - assertThat(rule3.isDefined(), is(true)); - assertThat(rule3.get().getNameRegex(), is("\\d*")); - assertThat(rule3.get().getStageRegex(), is("\\d*")); - assertThat(rule3.get().getChannel(), is("ch3")); - - Option rule4 = rules.find("pipeline1", "stage1", Status.Passed.getStatus()); - assertThat(rule4.isDefined(), is(false)); + List foundRules1 = rules.find("abc", "efg", Status.Building.getStatus()); + assertThat(foundRules1.size(), is(1)); + assertThat(foundRules1.get(0).getNameRegex(), is("[a-z]*")); + assertThat(foundRules1.get(0).getStageRegex(), is("[a-z]*")); + assertThat(foundRules1.get(0).getChannel(), is("ch1")); + + List foundRules2 = rules.find("123", "456", Status.Building.getStatus()); + assertThat(foundRules2.size(), is(1)); + assertThat(foundRules2.get(0).getNameRegex(), is("\\d*")); + assertThat(foundRules2.get(0).getStageRegex(), is("\\d*")); + assertThat(foundRules2.get(0).getChannel(), is("ch2")); + + List foundRules3 = rules.find("123", "456", Status.Passed.getStatus()); + assertThat(foundRules3.size(), is(1)); + assertThat(foundRules3.get(0).getNameRegex(), is("\\d*")); + assertThat(foundRules3.get(0).getStageRegex(), is("\\d*")); + assertThat(foundRules3.get(0).getChannel(), is("ch3")); + + List foundRules4 = rules.find("pipeline1", "stage1", Status.Passed.getStatus()); + assertThat(foundRules4.size(), is(0)); + } + @Test + public void shouldFindAllMatchesIfProcessAllRules() { + Rules rules = new Rules(); + rules.setProcessAllRules(true); + + rules.setPipelineRules(Arrays.asList( + pipelineRule("[a-z]*", "stage\\d+", "ch1", statuses(PipelineStatus.BUILDING)), + pipelineRule("[a-z]*", "stage2", "ch2", statuses(PipelineStatus.BUILDING)) + )); + + List foundRules1 = rules.find("abc", "stage1", Status.Building.getStatus()); + assertThat(foundRules1.size(), is(1)); + assertThat(foundRules1.get(0).getChannel(), is("ch1")); + + List foundRules2 = rules.find("abc", "stage2", Status.Building.getStatus()); + assertThat(foundRules2.size(), is(2)); + assertThat(foundRules2.get(0).getChannel(), is("ch1")); + assertThat(foundRules2.get(1).getChannel(), is("ch2")); + + List foundRules3 = rules.find("abc1", "stage2", Status.Building.getStatus()); + assertThat(foundRules3.size(), is(0)); } @Test public void shouldFindMatchAll() { Rules rules = new Rules(); - PipelineRule pRules1 = new PipelineRule("pipeline", "stage"); - pRules1.setStatus(new HashSet(Arrays.asList(PipelineStatus.PASSED, PipelineStatus.FAILED))); rules.setPipelineRules(Arrays.asList( pipelineRule("p1", "s1", "ch1", statuses(PipelineStatus.ALL)) )); - assertThat(rules.find("p1", "s1", Status.Building.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Broken.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Cancelled.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Failed.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Failing.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Fixed.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Passed.getStatus()).isDefined(), is(true)); - assertThat(rules.find("p1", "s1", Status.Unknown.getStatus()).isDefined(), is(true)); + assertThat(rules.find("p1", "s1", Status.Building.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Broken.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Cancelled.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Failed.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Failing.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Fixed.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Passed.getStatus()).size(), is(1)); + assertThat(rules.find("p1", "s1", Status.Unknown.getStatus()).size(), is(1)); } @Test @@ -117,4 +133,4 @@ private static Set statuses(PipelineStatus... statuses) { return new HashSet(Arrays.asList(statuses)); } -} \ No newline at end of file +}