Skip to content

Commit 3b539bd

Browse files
authored
Merge pull request #4 from hidapple/open-graylog
Open graylog
2 parents 988763d + 7cf7520 commit 3b539bd

File tree

10 files changed

+163
-45
lines changed

10 files changed

+163
-45
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,15 @@ Create Graylog notification of your stream and choose `Microsoft Teams Alarm Cal
3131
#### 3. Configure Microsoft Teams Alarm Callback
3232
Input your Teams incoming webhook published at #1 and fill out other configurations. Here is a screenshot of configuration example.
3333

34-
3534
![Teams notification configuraiton](img/configuration.png)
3635

3736
#### 4. Receive notification
38-
You will receive notification message.
37+
You will receive notification message like below.
3938

4039
![Teams notification message](img/message.png)
4140

42-
Getting started
43-
---------------
41+
Getting development started
42+
---------------------------
4443

4544
This project is using Maven 3 and requires Java 8 or higher.
4645

img/configuration.png

-64.6 KB
Loading

img/message.png

58.3 KB
Loading

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>org.graylog.plugins</groupId>
88
<artifactId>graylog-plugin-teams</artifactId>
9-
<version>1.0.0</version>
9+
<version>1.1.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>${project.artifactId}</name>

src/main/java/org/graylog/plugins/teams/alerts/TeamsNotification.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
import com.floreysoft.jmte.Engine;
44
import com.google.common.collect.Lists;
5-
import java.net.URI;
6-
import java.net.URISyntaxException;
7-
import java.util.Collections;
8-
import java.util.HashMap;
9-
import java.util.List;
10-
import java.util.Map;
11-
import java.util.Objects;
125
import org.graylog.plugins.teams.client.TeamsClient;
136
import org.graylog.plugins.teams.client.TeamsClientException;
147
import org.graylog.plugins.teams.client.TeamsMessageCard;
@@ -28,6 +21,14 @@
2821
import org.slf4j.Logger;
2922
import org.slf4j.LoggerFactory;
3023

24+
import java.net.URI;
25+
import java.net.URISyntaxException;
26+
import java.util.Collections;
27+
import java.util.HashMap;
28+
import java.util.List;
29+
import java.util.Map;
30+
import java.util.Objects;
31+
3132
/**
3233
* TeamsNotification is Graylog Notification(AlarmCallback) Plugin.
3334
*/
@@ -55,8 +56,10 @@ public void call(Stream stream, AlertCondition.CheckResult result) throws AlarmC
5556
configuration.getString(TeamsNotificationConfig.COLOR),
5657
"Alert for Graylog stream: " + stream.getTitle(),
5758
result.getResultDescription(),
58-
buildDetailMsg(stream, result, configuration.getString(TeamsNotificationConfig.DETAIL_MESSAGE))
59+
buildDetailMsg(stream, result, configuration.getString(TeamsNotificationConfig.DETAIL_MESSAGE)),
60+
configuration.getString(TeamsNotificationConfig.GRAYLOG_URL)
5961
);
62+
System.out.println(req.toJsonString());
6063
try {
6164
client.postMessageCard(req);
6265
} catch(TeamsClientException ex) {
@@ -74,6 +77,12 @@ public ConfigurationRequest getRequestedConfiguration() {
7477
"Microsoft Teams Incoming Webhook URL",
7578
Optional.NOT_OPTIONAL));
7679

80+
configRequest.addField(new TextField(
81+
TeamsNotificationConfig.GRAYLOG_URL, "Graylog URL",
82+
"",
83+
"URL to be attached in notification",
84+
Optional.OPTIONAL));
85+
7786
configRequest.addField(new TextField(
7887
TeamsNotificationConfig.COLOR, "Color",
7988
"0076D7",
@@ -97,7 +106,7 @@ public ConfigurationRequest getRequestedConfiguration() {
97106
"${else}" +
98107
"<No backlog>\n" +
99108
"${end}",
100-
"Detail message. Basic Markdown syntax is acceptable.",
109+
"Detail message supporting basic Markdown syntax",
101110
Optional.OPTIONAL,
102111
Attribute.TEXTAREA));
103112

@@ -127,6 +136,7 @@ public void checkConfiguration() throws ConfigurationException {
127136
}
128137
validateURI(configuration, TeamsNotificationConfig.WEBHOOK_URL);
129138
validateURI(configuration, TeamsNotificationConfig.PROXY);
139+
validateURI(configuration, TeamsNotificationConfig.GRAYLOG_URL);
130140

131141
// Not error but warning
132142
if (configuration.stringIsSet(TeamsNotificationConfig.COLOR)) {
@@ -175,10 +185,15 @@ private Map<String, Object> getModel(Stream stream, AlertCondition.CheckResult r
175185

176186
private void validateURI(Configuration config, String field) throws ConfigurationException {
177187
if (!config.stringIsSet(field)) return;
188+
189+
String uri = config.getString(field);
178190
try {
179-
new URI(Objects.requireNonNull(config.getString(field)));
191+
new URI(Objects.requireNonNull(uri));
180192
} catch (URISyntaxException ex) {
181193
throw new ConfigurationException(field + " is invalid as URI");
182194
}
195+
if (!uri.startsWith("http://") && !uri.startsWith("https://")) {
196+
throw new ConfigurationException(field + " supports only http(s)");
197+
}
183198
}
184199
}

src/main/java/org/graylog/plugins/teams/alerts/TeamsNotificationConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ public class TeamsNotificationConfig {
44

55
public static final String WEBHOOK_URL = "webhook_url";
66

7+
public static final String GRAYLOG_URL = "graylog_url";
8+
79
public static final String COLOR = "color";
810

911
public static final String DETAIL_MESSAGE = "detail_message";

src/main/java/org/graylog/plugins/teams/client/TeamsMessageCard.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import com.fasterxml.jackson.core.JsonProcessingException;
99
import com.fasterxml.jackson.databind.ObjectMapper;
1010
import com.google.common.collect.Lists;
11+
import org.apache.commons.lang.StringUtils;
12+
1113
import java.util.HashMap;
1214
import java.util.List;
1315
import java.util.Map;
1416
import java.util.Objects;
15-
import org.apache.commons.lang.StringUtils;
1617

1718
/**
1819
* MessageCard is representing Outlook Actionable Message Card request.
@@ -26,8 +27,9 @@ public class TeamsMessageCard {
2627
private String title;
2728
private String text;
2829
private List<Section> sections;
30+
private List<PotentialAction> potentialAction;
2931

30-
public TeamsMessageCard(String color, String title, String text, String detailMsg) {
32+
public TeamsMessageCard(String color, String title, String text, String detailMsg, String url) {
3133
this.type = "MessageCard";
3234
this.context = "https://schema.org/extensions";
3335
this.themeColor = color;
@@ -36,6 +38,13 @@ public TeamsMessageCard(String color, String title, String text, String detailMs
3638
if (!StringUtils.isEmpty(detailMsg)) {
3739
this.sections = Lists.newArrayList(new Section("Detail Message:", detailMsg));
3840
}
41+
if (!StringUtils.isEmpty(url)) {
42+
Map<String, String> target = new HashMap<>();
43+
target.put("os", "default");
44+
target.put("uri", url);
45+
this.potentialAction = Lists.newArrayList(
46+
new PotentialAction("OpenUri", "Open Graylog", Lists.newArrayList(target)));
47+
}
3948
}
4049

4150
public String toJsonString() {
@@ -45,9 +54,12 @@ public String toJsonString() {
4554
params.put("themeColor", themeColor);
4655
params.put("title", title);
4756
params.put("text", text);
48-
if (Objects.nonNull(this.sections)) {
57+
if (Objects.nonNull(sections)) {
4958
params.put("sections", sections);
5059
}
60+
if (Objects.nonNull(potentialAction)) {
61+
params.put("potentialAction", potentialAction);
62+
}
5163

5264
try {
5365
return new ObjectMapper().writeValueAsString(params);
@@ -70,4 +82,23 @@ public Section(String title, String text) {
7082
this.text = text;
7183
}
7284
}
85+
86+
@JsonInclude(Include.NON_NULL)
87+
@JsonIgnoreProperties(ignoreUnknown = true)
88+
public static class PotentialAction {
89+
@JsonProperty("@type")
90+
String type;
91+
@JsonProperty("name")
92+
String name;
93+
@JsonProperty("targets")
94+
List<Map<String, String>> targets;
95+
96+
@JsonCreator
97+
PotentialAction(String type, String name, List<Map<String, String>> targets) {
98+
this.type = type;
99+
this.name = name;
100+
this.targets = targets;
101+
}
102+
}
103+
73104
}

src/test/java/org/graylog/plugins/teams/alerts/TeamsNotificationTest.java

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
package org.graylog.plugins.teams.alerts;
22

3-
import static org.junit.jupiter.api.Assertions.assertEquals;
4-
import static org.junit.jupiter.api.Assertions.assertThrows;
5-
import static org.junit.jupiter.api.Assertions.assertTrue;
6-
import static org.junit.jupiter.api.Assertions.fail;
7-
83
import com.google.common.collect.Lists;
9-
import java.util.HashMap;
10-
import java.util.List;
11-
import java.util.Map;
124
import org.apache.commons.lang.StringUtils;
135
import org.graylog2.plugin.alarms.callbacks.AlarmCallbackConfigurationException;
146
import org.graylog2.plugin.configuration.Configuration;
@@ -17,6 +9,15 @@
179
import org.junit.jupiter.api.BeforeEach;
1810
import org.junit.jupiter.api.Test;
1911

12+
import java.util.HashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
16+
import static org.junit.jupiter.api.Assertions.assertEquals;
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
18+
import static org.junit.jupiter.api.Assertions.assertTrue;
19+
import static org.junit.jupiter.api.Assertions.fail;
20+
2021
class TeamsNotificationTest {
2122

2223
private TeamsNotification sut;
@@ -40,14 +41,15 @@ void getAttribute() throws AlarmCallbackConfigurationException {
4041
void getRequestedConfiguration() {
4142
List<String> expectedConfigFields = Lists.newArrayList(
4243
TeamsNotificationConfig.WEBHOOK_URL,
44+
TeamsNotificationConfig.GRAYLOG_URL,
4345
TeamsNotificationConfig.COLOR,
4446
TeamsNotificationConfig.DETAIL_MESSAGE,
4547
TeamsNotificationConfig.PROXY
4648
);
4749

4850
Map<String, ConfigurationField> actual = sut.getRequestedConfiguration().getFields();
4951

50-
assertEquals(4, actual.size());
52+
assertEquals(expectedConfigFields.size(), actual.size());
5153
expectedConfigFields.forEach(
5254
expected -> assertTrue(actual.containsKey(expected)));
5355
}
@@ -92,6 +94,16 @@ void checkConfiguration_Fail_WebhookURLIsInvalid() throws AlarmCallbackConfigura
9294
assertEquals(TeamsNotificationConfig.WEBHOOK_URL + " is invalid as URI", ex.getMessage());
9395
}
9496

97+
@Test
98+
void checkConfiguration_Fail_WebhookURLIsUnsupportedProtocol() throws AlarmCallbackConfigurationException {
99+
Map<String, Object> m = createValidConfigMap();
100+
m.replace(TeamsNotificationConfig.WEBHOOK_URL, "ftp://localhost");
101+
sut.initialize(new Configuration(m));
102+
103+
ConfigurationException ex = assertThrows(ConfigurationException.class, () -> sut.checkConfiguration());
104+
assertEquals(TeamsNotificationConfig.WEBHOOK_URL + " supports only http(s)", ex.getMessage());
105+
}
106+
95107
@Test
96108
void checkConfiguration_Fail_ProxyURLIsInvalid() throws AlarmCallbackConfigurationException {
97109
Map<String, Object> m = createValidConfigMap();
@@ -102,9 +114,40 @@ void checkConfiguration_Fail_ProxyURLIsInvalid() throws AlarmCallbackConfigurati
102114
assertEquals(TeamsNotificationConfig.PROXY + " is invalid as URI", ex.getMessage());
103115
}
104116

117+
@Test
118+
void checkConfiguration_Fail_ProxyURLIsUnsupportedProtocol() throws AlarmCallbackConfigurationException {
119+
Map<String, Object> m = createValidConfigMap();
120+
m.replace(TeamsNotificationConfig.PROXY, "ftp://localhost");
121+
sut.initialize(new Configuration(m));
122+
123+
ConfigurationException ex = assertThrows(ConfigurationException.class, () -> sut.checkConfiguration());
124+
assertEquals(TeamsNotificationConfig.PROXY + " supports only http(s)", ex.getMessage());
125+
}
126+
127+
@Test
128+
void checkConfiguration_Fail_GraylogURLIsInvalid() throws AlarmCallbackConfigurationException {
129+
Map<String, Object> m = createValidConfigMap();
130+
m.replace(TeamsNotificationConfig.GRAYLOG_URL, "invalid URL");
131+
sut.initialize(new Configuration(m));
132+
133+
ConfigurationException ex = assertThrows(ConfigurationException.class, () -> sut.checkConfiguration());
134+
assertEquals(TeamsNotificationConfig.GRAYLOG_URL + " is invalid as URI", ex.getMessage());
135+
}
136+
137+
@Test
138+
void checkConfiguration_Fail_GraylogURLIsUnsupportedProtocol() throws AlarmCallbackConfigurationException {
139+
Map<String, Object> m = createValidConfigMap();
140+
m.replace(TeamsNotificationConfig.GRAYLOG_URL, "ftp://localhost");
141+
sut.initialize(new Configuration(m));
142+
143+
ConfigurationException ex = assertThrows(ConfigurationException.class, () -> sut.checkConfiguration());
144+
assertEquals(TeamsNotificationConfig.GRAYLOG_URL + " supports only http(s)", ex.getMessage());
145+
}
146+
105147
private Map<String, Object> createValidConfigMap() {
106148
Map<String, Object> m = new HashMap<>();
107149
m.put(TeamsNotificationConfig.WEBHOOK_URL, "https://testwebhook.com");
150+
m.put(TeamsNotificationConfig.GRAYLOG_URL, "https://my-graylog.com");
108151
m.put(TeamsNotificationConfig.COLOR, "000000");
109152
m.put(TeamsNotificationConfig.DETAIL_MESSAGE, "Detail");
110153
m.put(TeamsNotificationConfig.PROXY, "http://proxy.com:9999");

src/test/java/org/graylog/plugins/teams/client/TeamsClientTest.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
package org.graylog.plugins.teams.client;
22

3-
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
4-
import static com.github.tomakehurst.wiremock.client.WireMock.post;
5-
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
6-
import static org.junit.jupiter.api.Assertions.assertEquals;
7-
import static org.junit.jupiter.api.Assertions.assertThrows;
8-
import static org.junit.jupiter.api.Assertions.fail;
9-
103
import com.github.tomakehurst.wiremock.WireMockServer;
11-
import java.util.HashMap;
12-
import java.util.Map;
134
import org.graylog.plugins.teams.alerts.TeamsNotificationConfig;
145
import org.graylog2.plugin.configuration.Configuration;
156
import org.junit.jupiter.api.AfterEach;
167
import org.junit.jupiter.api.BeforeEach;
178
import org.junit.jupiter.api.Nested;
189
import org.junit.jupiter.api.Test;
1910

11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
15+
import static com.github.tomakehurst.wiremock.client.WireMock.post;
16+
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
18+
import static org.junit.jupiter.api.Assertions.assertThrows;
19+
import static org.junit.jupiter.api.Assertions.fail;
20+
2021
class TeamsClientTest {
2122

2223
@Test
@@ -99,7 +100,7 @@ void postMessageCard() {
99100

100101
// Then
101102
try {
102-
sut.postMessageCard(new TeamsMessageCard("FFFFFF", "Title", "Text", "Detail"));
103+
sut.postMessageCard(new TeamsMessageCard("FFFFFF", "Title", "Text", "Detail", ""));
103104
} catch (Exception ex) {
104105
fail("Exception should not be thrown.", ex);
105106
}
@@ -119,7 +120,7 @@ void postMessageCard_Fail_UnexpectedRequestCode() {
119120

120121
// Then
121122
TeamsClientException ex = assertThrows(TeamsClientException.class,
122-
() -> sut.postMessageCard(new TeamsMessageCard("FFFFFF", "Title", "Text", "Detail")));
123+
() -> sut.postMessageCard(new TeamsMessageCard("FFFFFF", "Title", "Text", "Detail", "")));
123124
assertEquals("Teams webhook returned unexpected response status. HTTP Status=500", ex.getMessage());
124125
}
125126
}

0 commit comments

Comments
 (0)