Skip to content

Commit

Permalink
Added tests and documentation for JMTE-Template resolving #7
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanHufschmidt committed Jun 24, 2019
1 parent 3779f50 commit ad5bce4
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
V 1.2
-----------
* Added JMTE-Template resolving to values for custom labels and annotations

V 1.1
-----------
* Added release artifact to GitHub releases
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ The values `startsAt`, `endsAt` and `generatorURL` will be transmitted to the Al
`endsAt` will be set to the point of time when the condition triggered the alert plus the set grace time which is configured for the alert.

Additionally you can configure your own custom annotations and labels which should be submitted to the AlertManager (see screenshot below).
You can use the [JMTE Template](https://cdn.rawgit.com/DJCordhose/jmte/master/doc/index.html) as you might already know from the [Graylog E-Mail Notification Callback](http://docs.graylog.org/en/2.5/pages/streams/alerts.html#email-alert-notification).

List of provided keys you can use inside JMTE Templates:
* `stream_url` - The stream url.
* `stream` - The specific stream object. There you can use the properties of the stream object e.g. `stream.title`
* `alertCondition` - The specific triggered alert condition. There you can use the properties of the alert condition oject e.g. `alertCondition.createdAt`
* `check_result` - The specific check result. There you can use the properties of the check result object e.g. `check_result.triggeredAt`
* `backlog` - A list containing messages matching the triggered condition if any. You can iterate through them with `${foreach backlog message}${message} ${end}`
* `backlog_size` - The amount of matching messages.

## How to deploy on Graylog
You can easily build the plugin by executing `./gradlew build -x check --no-daemon`.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ serviceLoader {
}

group = 'de.gdata.mobilelab'
version = 1.1
version = 1.2

sourceCompatibility = 1.8
targetCompatibility = 1.8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ public ConfigurationRequest getRequestedConfiguration() {
CONFIGURATION_KEY_CUSTOM_LABELS,
"Custom AlertManager labels",
"",
"The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "label2=value2'",
"The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR
+ "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR
+ "label2=value2"+ CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "streamtitle=${stream.title}'. You can use the JMTE-Template annotation within values of your custom labels to get live information from triggered alert.",
Optional.OPTIONAL
);
configurationRequest.addField(customLabels);
Expand All @@ -83,7 +85,9 @@ public ConfigurationRequest getRequestedConfiguration() {
CONFIGURATION_KEY_CUSTOM_ANNOTATIONS,
"Custom AlertManager annotations",
"",
"The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "annotation2=value2'",
"The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR
+ "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR
+ "annotation2=value2" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "streamtitle=${stream.title}'. You can use the JMTE-Template annotation within values of your custom annotations to get live information from triggered alert.",
Optional.OPTIONAL
);
configurationRequest.addField(customAnnotations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public URI getURL() {

@Override
public Version getVersion() {
return Version.from(1, 0, 0);
return Version.from(1, 2, 0);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
import java.lang.reflect.Field;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -75,15 +73,15 @@ public void getRequestedConfiguration() {
assertEquals(TextField.FIELD_TYPE, field.getFieldType());
assertEquals("Custom AlertManager labels", field.getHumanName());
assertEquals("", field.getDefaultValue());
assertEquals("The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "label2=value2'", field.getDescription());
assertEquals("The custom AlertManager label key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'label1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "label2=value2" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "streamtitle=${stream.title}'. You can use the JMTE-Template annotation within values of your custom labels to get live information from triggered alert.", field.getDescription());
assertEquals(ConfigurationField.Optional.OPTIONAL, field.isOptional());

field = configurationRequest.getField("alertmanager_custom_annotations");
assertNotNull(field);
assertEquals(TextField.FIELD_TYPE, field.getFieldType());
assertEquals("Custom AlertManager annotations", field.getHumanName());
assertEquals("", field.getDefaultValue());
assertEquals("The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "annotation2=value2'", field.getDescription());
assertEquals("The custom AlertManager annotation key-value-pairs separated by '" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "' to set for each alert. Please use the following notation: 'annotation1=value1" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "annotation2=value2" + CustomPropertiesTextFieldParser.KEY_VALUE_PAIR_SEPARATOR + "streamtitle=${stream.title}'. You can use the JMTE-Template annotation within values of your custom annotations to get live information from triggered alert.", field.getDescription());
assertEquals(ConfigurationField.Optional.OPTIONAL, field.isOptional());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void getURL() {

@Test
public void getVersion() {
assertEquals(Version.from(1, 0, 0), new AlertManagerPluginMetaData().getVersion());
assertEquals(Version.from(1, 2, 0), new AlertManagerPluginMetaData().getVersion());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,249 @@
package de.gdata.mobilelab.alertmanagercallback;

import org.graylog2.plugin.Message;
import org.graylog2.plugin.MessageSummary;
import org.graylog2.plugin.alarms.AlertCondition;
import org.graylog2.plugin.streams.Stream;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Test;

import java.lang.reflect.Field;
import java.util.*;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class CustomPropertiesJMTEResolverTest {

// TODO: Add tests
@Test
public void testBuilder() {
// when: building
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().build();

// then: should not be null
assertNotNull(customPropertiesJMTEResolver);
}

@Test
public void testBuilderWithUrl() throws Exception {
// given: An URL
String url = "https://somealertmanager.alert/";

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given url
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withUrl(url).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with URL
assertEquals(url, templateModel.get("stream_url"));
}

@Test
public void testBuilderWithStream() throws Exception {
// given: A Stream
Stream stream = mock(Stream.class);

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given stream
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withStream(stream).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with stream
assertEquals(stream, templateModel.get("stream"));
}

@Test
public void testBuilderWithCheckResult() throws Exception {
// given: A CheckResult
AlertCondition.CheckResult checkResult = mock(AlertCondition.CheckResult.class);

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given checkResult
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withCheckResult(checkResult).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with checkResult
assertEquals(checkResult, templateModel.get("check_result"));
}

@Test
public void testBuilderWithCheckResultContainingMessages() throws Exception {
// given: A CheckResult
AlertCondition.CheckResult checkResult = mock(AlertCondition.CheckResult.class);

// and: Message Mocks
Message messageOne = new Message("messageOne", "source", new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC));
Message messageTwo = new Message("messageTwo", "source", new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC));
MessageSummary messageSummaryOne = mock(MessageSummary.class);
MessageSummary messageSummaryTwo = mock(MessageSummary.class);
List<MessageSummary> messageSummaryList = Arrays.asList(messageSummaryOne, messageSummaryTwo);
when(messageSummaryOne.getRawMessage()).thenReturn(messageOne);
when(messageSummaryTwo.getRawMessage()).thenReturn(messageTwo);
when(checkResult.getMatchingMessages()).thenReturn(messageSummaryList);

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given checkResult
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withCheckResult(checkResult).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with checkResult
assertEquals(checkResult, templateModel.get("check_result"));

// and: templateModel contains those messages from above
assertEquals(2, templateModel.get("backlog_size"));
assertEquals(2, ((List<Message>) templateModel.get("backlog")).size());
assertTrue(((List<Message>) templateModel.get("backlog")).contains(messageOne));
assertTrue(((List<Message>) templateModel.get("backlog")).contains(messageTwo));
}

@Test
public void testBuilderWithCheckResultContainingNoMatchingMessages() throws Exception {
// given: A CheckResult which returns null as matching messages
AlertCondition.CheckResult checkResult = mock(AlertCondition.CheckResult.class);
when(checkResult.getMatchingMessages()).thenReturn(null);

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given checkResult
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withCheckResult(checkResult).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with checkResult
assertEquals(checkResult, templateModel.get("check_result"));

// and: templateModel contains those messages from above
assertEquals(0, templateModel.get("backlog_size"));
assertTrue(((List<Message>) templateModel.get("backlog")).isEmpty());
}

@Test
public void testBuilderWithCheckResultContainingTriggeredCondition() throws Exception {
// given: A CheckResult which returns a triggered condition
AlertCondition.CheckResult checkResult = mock(AlertCondition.CheckResult.class);
AlertCondition triggeredCondition = mock(AlertCondition.class);
when(checkResult.getTriggeredCondition()).thenReturn(triggeredCondition);

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// when: building with given checkResult
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().withCheckResult(checkResult).build();
Map<String, Object> templateModel = (Map<String, Object>) templateModelField.get(customPropertiesJMTEResolver);

// then: templateModel should contain entry with checkResult
assertEquals(checkResult, templateModel.get("check_result"));

// and: templateModel contains an alertCondition
assertEquals(triggeredCondition, templateModel.get("alertCondition"));
}

@Test
public void testTransformTemplateValuesWithEmptyTemplateModelMap() throws Exception {
// given: an empty Map
Map<String, Object> emptyMap = Collections.emptyMap();

// and: a CustomPropertiesJMTEResolver instance
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().build();

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// and: resolver has given map
templateModelField.set(customPropertiesJMTEResolver, emptyMap);

// and: A custom value map
Map<String, Object> customValueMap = new HashMap<>();
customValueMap.put("test1", "${a_unknown_key_for_template}");
customValueMap.put("test2", "${a_unknown_key_for_template2}sometext${another_one}");
customValueMap.put("test3", 1337);

// when: calling transformTemplateValues
Map<String, Object> result = customPropertiesJMTEResolver.transformTemplateValues(customValueMap);

// then: values which do not exist will be replaced with an empty string
assertTrue(result.containsKey("test1"));
assertTrue(result.containsKey("test2"));
assertTrue(result.containsKey("test3"));
assertEquals("", result.get("test1"));
assertEquals("sometext", result.get("test2"));
assertEquals(1337, result.get("test3"));
}

@Test
public void testTransformTemplateValuesWithCustomTemplateModelMap() throws Exception {
// given: a custom templateModelMap
Map<String, Object> customTemplateModelMap = new HashMap<>();
customTemplateModelMap.put("a_known_key_for_template", "bla");
customTemplateModelMap.put("a_known_key_for_template2", "blubb");
customTemplateModelMap.put("another_one", "hello");
customTemplateModelMap.put("backlog", Arrays.asList("Message 1: Hello", "Message2: World"));
customTemplateModelMap.put("existing_value", "Hello World!");

// and: a CustomPropertiesJMTEResolver instance
CustomPropertiesJMTEResolver customPropertiesJMTEResolver = CustomPropertiesJMTEResolver
.Builder.newBuilder().build();

// and: access to private field for templateModel
Field templateModelField = CustomPropertiesJMTEResolver.class.getDeclaredField("templateModel");
templateModelField.setAccessible(true);

// and: resolver has given map
templateModelField.set(customPropertiesJMTEResolver, customTemplateModelMap);

// and: A custom value map
Map<String, Object> customValueMap = new HashMap<>();
customValueMap.put("test1", "${a_known_key_for_template}");
customValueMap.put("test2", "${a_known_key_for_template2}sometext${another_one}");
customValueMap.put("test3", 1337);
customValueMap.put("test4", "${an_unknown_key_for_template}");
customValueMap.put("foreachTest", "${foreach backlog message}${message} ${end}");
customValueMap.put("ifTest", "${if not_existing_value}${not_existing_value}${else}unknown${end}");
customValueMap.put("ifTest2", "${if existing_value}${existing_value}${else}unknown${end}");

// when: calling transformTemplateValues
Map<String, Object> result = customPropertiesJMTEResolver.transformTemplateValues(customValueMap);

// then: values should be replaced if they exist inside the templateModelMap
assertTrue(result.containsKey("test1"));
assertTrue(result.containsKey("test2"));
assertTrue(result.containsKey("test3"));
assertTrue(result.containsKey("test4"));
assertTrue(result.containsKey("foreachTest"));
assertTrue(result.containsKey("ifTest"));
assertTrue(result.containsKey("ifTest2"));
assertEquals("bla", result.get("test1"));
assertEquals("blubbsometexthello", result.get("test2"));
assertEquals(1337, result.get("test3"));
assertEquals("", result.get("test4"));
assertEquals("Message 1: Hello Message2: World ", result.get("foreachTest"));
assertEquals("unknown", result.get("ifTest"));
assertEquals("Hello World!", result.get("ifTest2"));
}

}

0 comments on commit ad5bce4

Please sign in to comment.