* Watcher add email warning if CSV attachment contains formulas (#44460) This commit introduces a Warning message to the emails generated by Watcher's reporting action. This change complements Kibana's CSV formula notifications (see elastic/kibana#37930). This is implemented by reading a header (kbn-csv-contains-formulas) provided by Kibana to notify to attach the Warning to the email. The wording of the warning is borrowed from Kibana's UI and may be overridden by a dynamic setting xpack.notification.reporting.warning.kbn-csv-contains-formulas.text. This warning is enabled by default, but may be disabled via a dynamic setting xpack.notification.reporting.warning.enabled.
This commit is contained in:
parent
f2241a152f
commit
767f648f8e
|
@ -41,7 +41,7 @@ dependencies {
|
||||||
|
|
||||||
testCompile 'org.subethamail:subethasmtp:3.1.7'
|
testCompile 'org.subethamail:subethasmtp:3.1.7'
|
||||||
// needed for subethasmtp, has @GuardedBy annotation
|
// needed for subethasmtp, has @GuardedBy annotation
|
||||||
testCompile 'com.google.code.findbugs:jsr305:3.0.1'
|
testCompile 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
// classes are missing, e.g. com.ibm.icu.lang.UCharacter
|
// classes are missing, e.g. com.ibm.icu.lang.UCharacter
|
||||||
|
|
|
@ -288,7 +288,8 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
|
||||||
Map<String, EmailAttachmentParser> emailAttachmentParsers = new HashMap<>();
|
Map<String, EmailAttachmentParser> emailAttachmentParsers = new HashMap<>();
|
||||||
emailAttachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, templateEngine));
|
emailAttachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, templateEngine));
|
||||||
emailAttachmentParsers.put(DataAttachmentParser.TYPE, new DataAttachmentParser());
|
emailAttachmentParsers.put(DataAttachmentParser.TYPE, new DataAttachmentParser());
|
||||||
emailAttachmentParsers.put(ReportingAttachmentParser.TYPE, new ReportingAttachmentParser(settings, httpClient, templateEngine));
|
emailAttachmentParsers.put(ReportingAttachmentParser.TYPE,
|
||||||
|
new ReportingAttachmentParser(settings, httpClient, templateEngine, clusterService.getClusterSettings()));
|
||||||
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(emailAttachmentParsers);
|
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(emailAttachmentParsers);
|
||||||
|
|
||||||
// conditions
|
// conditions
|
||||||
|
@ -487,8 +488,7 @@ public class Watcher extends Plugin implements ActionPlugin, ScriptPlugin, Reloa
|
||||||
settings.addAll(HtmlSanitizer.getSettings());
|
settings.addAll(HtmlSanitizer.getSettings());
|
||||||
settings.addAll(JiraService.getSettings());
|
settings.addAll(JiraService.getSettings());
|
||||||
settings.addAll(PagerDutyService.getSettings());
|
settings.addAll(PagerDutyService.getSettings());
|
||||||
settings.add(ReportingAttachmentParser.RETRIES_SETTING);
|
settings.addAll(ReportingAttachmentParser.getSettings());
|
||||||
settings.add(ReportingAttachmentParser.INTERVAL_SETTING);
|
|
||||||
|
|
||||||
// http settings
|
// http settings
|
||||||
settings.addAll(HttpSettings.getSettings());
|
settings.addAll(HttpSettings.getSettings());
|
||||||
|
|
|
@ -24,6 +24,8 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static javax.mail.Part.ATTACHMENT;
|
import static javax.mail.Part.ATTACHMENT;
|
||||||
import static javax.mail.Part.INLINE;
|
import static javax.mail.Part.INLINE;
|
||||||
|
@ -31,10 +33,17 @@ import static javax.mail.Part.INLINE;
|
||||||
public abstract class Attachment extends BodyPartSource {
|
public abstract class Attachment extends BodyPartSource {
|
||||||
|
|
||||||
private final boolean inline;
|
private final boolean inline;
|
||||||
|
private final Set<String> warnings;
|
||||||
|
|
||||||
protected Attachment(String id, String name, String contentType, boolean inline) {
|
protected Attachment(String id, String name, String contentType, boolean inline) {
|
||||||
|
this(id, name, contentType, inline, Collections.emptySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Attachment(String id, String name, String contentType, boolean inline, Set<String> warnings) {
|
||||||
super(id, name, contentType);
|
super(id, name, contentType);
|
||||||
this.inline = inline;
|
this.inline = inline;
|
||||||
|
assert warnings != null;
|
||||||
|
this.warnings = warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,6 +62,10 @@ public abstract class Attachment extends BodyPartSource {
|
||||||
return inline;
|
return inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<String> getWarnings() {
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* intentionally not emitting path as it may come as an information leak
|
* intentionally not emitting path as it may come as an information leak
|
||||||
*/
|
*/
|
||||||
|
@ -116,15 +129,15 @@ public abstract class Attachment extends BodyPartSource {
|
||||||
private final byte[] bytes;
|
private final byte[] bytes;
|
||||||
|
|
||||||
public Bytes(String id, byte[] bytes, String contentType, boolean inline) {
|
public Bytes(String id, byte[] bytes, String contentType, boolean inline) {
|
||||||
this(id, id, bytes, contentType, inline);
|
this(id, id, bytes, contentType, inline, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bytes(String id, String name, byte[] bytes, boolean inline) {
|
public Bytes(String id, String name, byte[] bytes, boolean inline) {
|
||||||
this(id, name, bytes, fileTypeMap.getContentType(name), inline);
|
this(id, name, bytes, fileTypeMap.getContentType(name), inline, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bytes(String id, String name, byte[] bytes, String contentType, boolean inline) {
|
public Bytes(String id, String name, byte[] bytes, String contentType, boolean inline, Set<String> warnings) {
|
||||||
super(id, name, contentType, inline);
|
super(id, name, contentType, inline, warnings);
|
||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +226,7 @@ public abstract class Attachment extends BodyPartSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected XContent(String id, String name, ToXContent content, XContentType type) {
|
protected XContent(String id, String name, ToXContent content, XContentType type) {
|
||||||
super(id, name, bytes(name, content, type), mimeType(type), false);
|
super(id, name, bytes(name, content, type), mimeType(type), false, Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
static String mimeType(XContentType type) {
|
static String mimeType(XContentType type) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.watcher.notification.email;
|
package org.elasticsearch.xpack.watcher.notification.email;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
@ -16,9 +17,11 @@ import javax.mail.internet.AddressException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class EmailTemplate implements ToXContentObject {
|
public class EmailTemplate implements ToXContentObject {
|
||||||
|
|
||||||
|
@ -110,19 +113,46 @@ public class EmailTemplate implements ToXContentObject {
|
||||||
if (subject != null) {
|
if (subject != null) {
|
||||||
builder.subject(engine.render(subject, model));
|
builder.subject(engine.render(subject, model));
|
||||||
}
|
}
|
||||||
if (textBody != null) {
|
|
||||||
builder.textBody(engine.render(textBody, model));
|
Set<String> warnings = new HashSet<>(1);
|
||||||
}
|
|
||||||
if (attachments != null) {
|
if (attachments != null) {
|
||||||
for (Attachment attachment : attachments.values()) {
|
for (Attachment attachment : attachments.values()) {
|
||||||
builder.attach(attachment);
|
builder.attach(attachment);
|
||||||
|
warnings.addAll(attachment.getWarnings());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String htmlWarnings = "";
|
||||||
|
String textWarnings = "";
|
||||||
|
if(warnings.isEmpty() == false){
|
||||||
|
StringBuilder textWarningBuilder = new StringBuilder();
|
||||||
|
StringBuilder htmlWarningBuilder = new StringBuilder();
|
||||||
|
warnings.forEach(w ->
|
||||||
|
{
|
||||||
|
if(Strings.isNullOrEmpty(w) == false) {
|
||||||
|
textWarningBuilder.append(w).append("\n");
|
||||||
|
htmlWarningBuilder.append(w).append("<br>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
textWarningBuilder.append("\n");
|
||||||
|
htmlWarningBuilder.append("<br>");
|
||||||
|
htmlWarnings = htmlWarningBuilder.toString();
|
||||||
|
textWarnings = textWarningBuilder.toString();
|
||||||
|
}
|
||||||
|
if (textBody != null) {
|
||||||
|
builder.textBody(textWarnings + engine.render(textBody, model));
|
||||||
|
}
|
||||||
|
|
||||||
if (htmlBody != null) {
|
if (htmlBody != null) {
|
||||||
String renderedHtml = engine.render(htmlBody, model);
|
String renderedHtml = htmlWarnings + engine.render(htmlBody, model);
|
||||||
renderedHtml = htmlSanitizer.sanitize(renderedHtml);
|
renderedHtml = htmlSanitizer.sanitize(renderedHtml);
|
||||||
builder.htmlBody(renderedHtml);
|
builder.htmlBody(renderedHtml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(htmlBody == null && textBody == null && Strings.isNullOrEmpty(textWarnings) == false){
|
||||||
|
builder.textBody(textWarnings);
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,16 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.watcher.notification.email.attachment;
|
package org.elasticsearch.xpack.watcher.notification.email.attachment;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
import org.elasticsearch.common.logging.LoggerMessageFormat;
|
||||||
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
@ -37,22 +40,39 @@ import org.elasticsearch.xpack.watcher.support.Variables;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UncheckedIOException;
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ReportingAttachmentParser implements EmailAttachmentParser<ReportingAttachment> {
|
public class ReportingAttachmentParser implements EmailAttachmentParser<ReportingAttachment> {
|
||||||
|
|
||||||
public static final String TYPE = "reporting";
|
public static final String TYPE = "reporting";
|
||||||
|
|
||||||
// total polling of 10 minutes happens this way by default
|
// total polling of 10 minutes happens this way by default
|
||||||
public static final Setting<TimeValue> INTERVAL_SETTING =
|
static final Setting<TimeValue> INTERVAL_SETTING =
|
||||||
Setting.timeSetting("xpack.notification.reporting.interval", TimeValue.timeValueSeconds(15), Setting.Property.NodeScope);
|
Setting.timeSetting("xpack.notification.reporting.interval", TimeValue.timeValueSeconds(15), Setting.Property.NodeScope);
|
||||||
public static final Setting<Integer> RETRIES_SETTING =
|
static final Setting<Integer> RETRIES_SETTING =
|
||||||
Setting.intSetting("xpack.notification.reporting.retries", 40, 0, Setting.Property.NodeScope);
|
Setting.intSetting("xpack.notification.reporting.retries", 40, 0, Setting.Property.NodeScope);
|
||||||
|
|
||||||
|
static final Setting<Boolean> REPORT_WARNING_ENABLED_SETTING =
|
||||||
|
Setting.boolSetting("xpack.notification.reporting.warning.enabled", true, Setting.Property.NodeScope, Setting.Property.Dynamic);
|
||||||
|
|
||||||
|
static final Setting.AffixSetting<String> REPORT_WARNING_TEXT =
|
||||||
|
Setting.affixKeySetting("xpack.notification.reporting.warning.", "text",
|
||||||
|
key -> Setting.simpleString(key, Setting.Property.NodeScope, Setting.Property.Dynamic));
|
||||||
|
|
||||||
private static final ObjectParser<Builder, AuthParseContext> PARSER = new ObjectParser<>("reporting_attachment");
|
private static final ObjectParser<Builder, AuthParseContext> PARSER = new ObjectParser<>("reporting_attachment");
|
||||||
private static final ObjectParser<KibanaReportingPayload, Void> PAYLOAD_PARSER =
|
private static final ObjectParser<KibanaReportingPayload, Void> PAYLOAD_PARSER =
|
||||||
new ObjectParser<>("reporting_attachment_kibana_payload", true, null);
|
new ObjectParser<>("reporting_attachment_kibana_payload", true, null);
|
||||||
|
|
||||||
|
static final Map<String, String> WARNINGS = ImmutableMap.of("kbn-csv-contains-formulas", "Warning: The attachment [%s] contains " +
|
||||||
|
"characters which spreadsheet applications may interpret as formulas. Please ensure that the attachment is safe prior to opening.");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareInt(Builder::retries, ReportingAttachment.RETRIES);
|
PARSER.declareInt(Builder::retries, ReportingAttachment.RETRIES);
|
||||||
PARSER.declareBoolean(Builder::inline, ReportingAttachment.INLINE);
|
PARSER.declareBoolean(Builder::inline, ReportingAttachment.INLINE);
|
||||||
|
@ -63,18 +83,52 @@ public class ReportingAttachmentParser implements EmailAttachmentParser<Reportin
|
||||||
PAYLOAD_PARSER.declareString(KibanaReportingPayload::setPath, new ParseField("path"));
|
PAYLOAD_PARSER.declareString(KibanaReportingPayload::setPath, new ParseField("path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<Setting<?>> getDynamicSettings() {
|
||||||
|
return Arrays.asList(REPORT_WARNING_ENABLED_SETTING, REPORT_WARNING_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Setting<?>> getStaticSettings() {
|
||||||
|
return Arrays.asList(INTERVAL_SETTING, RETRIES_SETTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Setting<?>> getSettings() {
|
||||||
|
List<Setting<?>> allSettings = new ArrayList<Setting<?>>(getDynamicSettings());
|
||||||
|
allSettings.addAll(getStaticSettings());
|
||||||
|
return allSettings;
|
||||||
|
}
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final TimeValue interval;
|
private final TimeValue interval;
|
||||||
private final int retries;
|
private final int retries;
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private final TextTemplateEngine templateEngine;
|
private final TextTemplateEngine templateEngine;
|
||||||
|
private boolean warningEnabled = REPORT_WARNING_ENABLED_SETTING.getDefault(Settings.EMPTY);
|
||||||
|
private final Map<String, String> customWarnings = new ConcurrentHashMap<>(1);
|
||||||
|
|
||||||
public ReportingAttachmentParser(Settings settings, HttpClient httpClient, TextTemplateEngine templateEngine) {
|
public ReportingAttachmentParser(Settings settings, HttpClient httpClient, TextTemplateEngine templateEngine,
|
||||||
|
ClusterSettings clusterSettings) {
|
||||||
this.interval = INTERVAL_SETTING.get(settings);
|
this.interval = INTERVAL_SETTING.get(settings);
|
||||||
this.retries = RETRIES_SETTING.get(settings);
|
this.retries = RETRIES_SETTING.get(settings);
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.templateEngine = templateEngine;
|
this.templateEngine = templateEngine;
|
||||||
this.logger = LogManager.getLogger(getClass());
|
this.logger = LogManager.getLogger(getClass());
|
||||||
|
clusterSettings.addSettingsUpdateConsumer(REPORT_WARNING_ENABLED_SETTING, this::setWarningEnabled);
|
||||||
|
clusterSettings.addAffixUpdateConsumer(REPORT_WARNING_TEXT, this::addWarningText, this::warningValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWarningEnabled(boolean warningEnabled) {
|
||||||
|
this.warningEnabled = warningEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addWarningText(String name, String value) {
|
||||||
|
customWarnings.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warningValidator(String name, String value) {
|
||||||
|
if (WARNINGS.keySet().contains(name) == false) {
|
||||||
|
throw new IllegalArgumentException(new ParameterizedMessage(
|
||||||
|
"Warning [{}] is not supported. Only the following warnings are supported [{}]",
|
||||||
|
name, String.join(", ", WARNINGS.keySet())).getFormattedMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -139,8 +193,24 @@ public class ReportingAttachmentParser implements EmailAttachmentParser<Reportin
|
||||||
"method[{}], path[{}], status[{}], body[{}]", context.watch().id(), attachment.id(), request.host(),
|
"method[{}], path[{}], status[{}], body[{}]", context.watch().id(), attachment.id(), request.host(),
|
||||||
request.port(), request.method(), request.path(), response.status(), body);
|
request.port(), request.method(), request.path(), response.status(), body);
|
||||||
} else if (response.status() == 200) {
|
} else if (response.status() == 200) {
|
||||||
return new Attachment.Bytes(attachment.id(), BytesReference.toBytes(response.body()),
|
Set<String> warnings = new HashSet<>(1);
|
||||||
response.contentType(), attachment.inline());
|
if (warningEnabled) {
|
||||||
|
WARNINGS.forEach((warningKey, defaultWarning) -> {
|
||||||
|
String[] text = response.header(warningKey);
|
||||||
|
if (text != null && text.length > 0) {
|
||||||
|
if (Boolean.valueOf(text[0])) {
|
||||||
|
String warning = String.format(Locale.ROOT, defaultWarning, attachment.id());
|
||||||
|
String customWarning = customWarnings.get(warningKey);
|
||||||
|
if (Strings.isNullOrEmpty(customWarning) == false) {
|
||||||
|
warning = String.format(Locale.ROOT, customWarning, attachment.id());
|
||||||
|
}
|
||||||
|
warnings.add(warning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Attachment.Bytes(attachment.id(), attachment.id(), BytesReference.toBytes(response.body()),
|
||||||
|
response.contentType(), attachment.inline(), warnings);
|
||||||
} else {
|
} else {
|
||||||
String body = response.body() != null ? response.body().utf8ToString() : null;
|
String body = response.body() != null ? response.body().utf8ToString() : null;
|
||||||
String message = LoggerMessageFormat.format("", "Watch[{}] reporting[{}] Unexpected status code host[{}], port[{}], " +
|
String message = LoggerMessageFormat.format("", "Watch[{}] reporting[{}] Unexpected status code host[{}], port[{}], " +
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.watcher.notification.email;
|
package org.elasticsearch.xpack.watcher.notification.email;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -13,18 +15,22 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.watcher.common.text.TextTemplate;
|
import org.elasticsearch.xpack.watcher.common.text.TextTemplate;
|
||||||
import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine;
|
import org.elasticsearch.xpack.watcher.test.MockTextTemplateEngine;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class EmailTemplateTests extends ESTestCase {
|
public class EmailTemplateTests extends ESTestCase {
|
||||||
|
@ -130,6 +136,90 @@ public class EmailTemplateTests extends ESTestCase {
|
||||||
assertValidEmail("{{valid due to mustache}}, lol.com");
|
assertValidEmail("{{valid due to mustache}}, lol.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEmailWarning() throws Exception {
|
||||||
|
TextTemplate from = randomFrom(new TextTemplate("from@from.com"), null);
|
||||||
|
List<TextTemplate> addresses = new ArrayList<>();
|
||||||
|
for (int i = 0; i < randomIntBetween(1, 5); ++i) {
|
||||||
|
addresses.add(new TextTemplate("address" + i + "@test.com"));
|
||||||
|
}
|
||||||
|
TextTemplate[] possibleList = addresses.toArray(new TextTemplate[addresses.size()]);
|
||||||
|
TextTemplate[] replyTo = randomFrom(possibleList, null);
|
||||||
|
TextTemplate[] to = randomFrom(possibleList, null);
|
||||||
|
TextTemplate[] cc = randomFrom(possibleList, null);
|
||||||
|
TextTemplate[] bcc = randomFrom(possibleList, null);
|
||||||
|
TextTemplate priority = new TextTemplate(randomFrom(Email.Priority.values()).name());
|
||||||
|
|
||||||
|
TextTemplate subjectTemplate = new TextTemplate("Templated Subject {{foo}}");
|
||||||
|
TextTemplate textBodyTemplate = new TextTemplate("Templated Body {{foo}}");
|
||||||
|
|
||||||
|
TextTemplate htmlBodyTemplate = new TextTemplate("Templated Html Body <script>nefarious scripting</script>");
|
||||||
|
String htmlBody = "Templated Html Body <script>nefarious scripting</script>";
|
||||||
|
String sanitizedHtmlBody = "Templated Html Body";
|
||||||
|
|
||||||
|
EmailTemplate emailTemplate = new EmailTemplate(from, replyTo, priority, to, cc, bcc, subjectTemplate, textBodyTemplate,
|
||||||
|
htmlBodyTemplate);
|
||||||
|
|
||||||
|
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||||
|
emailTemplate.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
|
|
||||||
|
XContentParser parser = createParser(builder);
|
||||||
|
parser.nextToken();
|
||||||
|
|
||||||
|
EmailTemplate.Parser emailTemplateParser = new EmailTemplate.Parser();
|
||||||
|
|
||||||
|
String currentFieldName = null;
|
||||||
|
XContentParser.Token token;
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
currentFieldName = parser.currentName();
|
||||||
|
} else {
|
||||||
|
assertThat(emailTemplateParser.handle(currentFieldName, parser), is(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EmailTemplate parsedEmailTemplate = emailTemplateParser.parsedTemplate();
|
||||||
|
|
||||||
|
Map<String, Object> model = new HashMap<>();
|
||||||
|
|
||||||
|
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||||
|
when(htmlSanitizer.sanitize(htmlBody)).thenReturn(sanitizedHtmlBody);
|
||||||
|
ArgumentCaptor<String> htmlSanitizeArguments = ArgumentCaptor.forClass(String.class);
|
||||||
|
|
||||||
|
//4 attachments, zero warning, one warning, two warnings, and one with html that should be stripped
|
||||||
|
Map<String, Attachment> attachments = ImmutableMap.of(
|
||||||
|
"one", new Attachment.Bytes("one", "one", randomByteArrayOfLength(100), randomAlphaOfLength(5), false, Collections.emptySet()),
|
||||||
|
"two", new Attachment.Bytes("two", "two", randomByteArrayOfLength(100), randomAlphaOfLength(5), false,
|
||||||
|
ImmutableSet.of("warning0")),
|
||||||
|
"thr", new Attachment.Bytes("thr", "thr", randomByteArrayOfLength(100), randomAlphaOfLength(5), false,
|
||||||
|
ImmutableSet.of("warning1", "warning2")),
|
||||||
|
"for", new Attachment.Bytes("for", "for", randomByteArrayOfLength(100), randomAlphaOfLength(5), false,
|
||||||
|
ImmutableSet.of("<script>warning3</script>")));
|
||||||
|
Email.Builder emailBuilder = parsedEmailTemplate.render(new MockTextTemplateEngine(), model, htmlSanitizer, attachments);
|
||||||
|
|
||||||
|
emailBuilder.id("_id");
|
||||||
|
Email email = emailBuilder.build();
|
||||||
|
assertThat(email.subject, equalTo(subjectTemplate.getTemplate()));
|
||||||
|
|
||||||
|
//text
|
||||||
|
int bodyStart = email.textBody.indexOf(textBodyTemplate.getTemplate());
|
||||||
|
String warnings = email.textBody.substring(0, bodyStart);
|
||||||
|
String[] warningLines = warnings.split("\n");
|
||||||
|
assertThat(warningLines.length, is(4));
|
||||||
|
for (int i = 0; i <= warningLines.length - 1; i++) {
|
||||||
|
assertThat(warnings, containsString("warning" + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
//html - pull the arguments as it is run through the sanitizer
|
||||||
|
verify(htmlSanitizer).sanitize(htmlSanitizeArguments.capture());
|
||||||
|
String fullHtmlBody = htmlSanitizeArguments.getValue();
|
||||||
|
bodyStart = fullHtmlBody.indexOf(htmlBodyTemplate.getTemplate());
|
||||||
|
warnings = fullHtmlBody.substring(0, bodyStart);
|
||||||
|
warningLines = warnings.split("<br>");
|
||||||
|
assertThat(warningLines.length, is(4));
|
||||||
|
for (int i = 0; i <= warningLines.length - 1; i++) {
|
||||||
|
assertThat(warnings, containsString("warning" + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void assertValidEmail(String email) {
|
private void assertValidEmail(String email) {
|
||||||
EmailTemplate.Parser.validateEmailAddresses(new TextTemplate(email));
|
EmailTemplate.Parser.validateEmailAddresses(new TextTemplate(email));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,11 @@
|
||||||
package org.elasticsearch.xpack.watcher.notification.email.attachment;
|
package org.elasticsearch.xpack.watcher.notification.email.attachment;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.io.JsonEOFException;
|
import com.fasterxml.jackson.core.io.JsonEOFException;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -38,11 +41,18 @@ import java.time.ZonedDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
|
import static org.elasticsearch.xpack.watcher.notification.email.attachment.ReportingAttachmentParser.INTERVAL_SETTING;
|
||||||
|
import static org.elasticsearch.xpack.watcher.notification.email.attachment.ReportingAttachmentParser.REPORT_WARNING_ENABLED_SETTING;
|
||||||
|
import static org.elasticsearch.xpack.watcher.notification.email.attachment.ReportingAttachmentParser.REPORT_WARNING_TEXT;
|
||||||
|
import static org.elasticsearch.xpack.watcher.notification.email.attachment.ReportingAttachmentParser.RETRIES_SETTING;
|
||||||
|
import static org.elasticsearch.xpack.watcher.notification.email.attachment.ReportingAttachmentParser.WARNINGS;
|
||||||
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContextBuilder;
|
import static org.elasticsearch.xpack.watcher.test.WatcherTestUtils.mockExecutionContextBuilder;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.hasEntry;
|
import static org.hamcrest.Matchers.hasEntry;
|
||||||
|
@ -66,12 +76,13 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
private ReportingAttachmentParser reportingAttachmentParser;
|
private ReportingAttachmentParser reportingAttachmentParser;
|
||||||
private MockTextTemplateEngine templateEngine = new MockTextTemplateEngine();
|
private MockTextTemplateEngine templateEngine = new MockTextTemplateEngine();
|
||||||
private String dashboardUrl = "http://www.example.org/ovb/api/reporting/generate/dashboard/My-Dashboard";
|
private String dashboardUrl = "http://www.example.org/ovb/api/reporting/generate/dashboard/My-Dashboard";
|
||||||
|
private ClusterSettings clusterSettings;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
httpClient = mock(HttpClient.class);
|
httpClient = mock(HttpClient.class);
|
||||||
reportingAttachmentParser = new ReportingAttachmentParser(Settings.EMPTY, httpClient, templateEngine);
|
clusterSettings = mockClusterService().getClusterSettings();
|
||||||
|
reportingAttachmentParser = new ReportingAttachmentParser(Settings.EMPTY, httpClient, templateEngine, clusterSettings);
|
||||||
attachmentParsers.put(ReportingAttachmentParser.TYPE, reportingAttachmentParser);
|
attachmentParsers.put(ReportingAttachmentParser.TYPE, reportingAttachmentParser);
|
||||||
emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
|
emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
|
||||||
}
|
}
|
||||||
|
@ -165,6 +176,7 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
new ReportingAttachment("foo", dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
new ReportingAttachment("foo", dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
||||||
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
||||||
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
||||||
|
assertThat(attachment.getWarnings(), hasSize(0));
|
||||||
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
||||||
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
||||||
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
||||||
|
@ -319,11 +331,11 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
.thenReturn(new HttpResponse(503));
|
.thenReturn(new HttpResponse(503));
|
||||||
|
|
||||||
ReportingAttachment attachment = new ReportingAttachment("foo", dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1),
|
ReportingAttachment attachment = new ReportingAttachment("foo", dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1),
|
||||||
ReportingAttachmentParser.RETRIES_SETTING.getDefault(Settings.EMPTY), new BasicAuth("foo", "bar".toCharArray()), null);
|
RETRIES_SETTING.getDefault(Settings.EMPTY), new BasicAuth("foo", "bar".toCharArray()), null);
|
||||||
expectThrows(ElasticsearchException.class, () ->
|
expectThrows(ElasticsearchException.class, () ->
|
||||||
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment));
|
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment));
|
||||||
|
|
||||||
verify(httpClient, times(ReportingAttachmentParser.RETRIES_SETTING.getDefault(Settings.EMPTY) + 1)).execute(any());
|
verify(httpClient, times(RETRIES_SETTING.getDefault(Settings.EMPTY) + 1)).execute(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPollingDefaultCanBeOverriddenBySettings() throws Exception {
|
public void testPollingDefaultCanBeOverriddenBySettings() throws Exception {
|
||||||
|
@ -335,11 +347,11 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
ReportingAttachment attachment = new ReportingAttachment("foo", dashboardUrl, randomBoolean(), null, null, null, null);
|
ReportingAttachment attachment = new ReportingAttachment("foo", dashboardUrl, randomBoolean(), null, null, null, null);
|
||||||
|
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.put(ReportingAttachmentParser.INTERVAL_SETTING.getKey(), "1ms")
|
.put(INTERVAL_SETTING.getKey(), "1ms")
|
||||||
.put(ReportingAttachmentParser.RETRIES_SETTING.getKey(), retries)
|
.put(RETRIES_SETTING.getKey(), retries)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
reportingAttachmentParser = new ReportingAttachmentParser(settings, httpClient, templateEngine);
|
reportingAttachmentParser = new ReportingAttachmentParser(settings, httpClient, templateEngine, clusterSettings);
|
||||||
expectThrows(ElasticsearchException.class, () ->
|
expectThrows(ElasticsearchException.class, () ->
|
||||||
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment));
|
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment));
|
||||||
|
|
||||||
|
@ -362,7 +374,7 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
ReportingAttachment attachment = new ReportingAttachment("foo", "http://www.example.org/REPLACEME", randomBoolean(),
|
ReportingAttachment attachment = new ReportingAttachment("foo", "http://www.example.org/REPLACEME", randomBoolean(),
|
||||||
TimeValue.timeValueMillis(1), 10, new BasicAuth("foo", "bar".toCharArray()), null);
|
TimeValue.timeValueMillis(1), 10, new BasicAuth("foo", "bar".toCharArray()), null);
|
||||||
reportingAttachmentParser = new ReportingAttachmentParser(Settings.EMPTY, httpClient,
|
reportingAttachmentParser = new ReportingAttachmentParser(Settings.EMPTY, httpClient,
|
||||||
replaceHttpWithHttpsTemplateEngine);
|
replaceHttpWithHttpsTemplateEngine, clusterSettings);
|
||||||
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment);
|
reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, attachment);
|
||||||
|
|
||||||
ArgumentCaptor<HttpRequest> requestArgumentCaptor = ArgumentCaptor.forClass(HttpRequest.class);
|
ArgumentCaptor<HttpRequest> requestArgumentCaptor = ArgumentCaptor.forClass(HttpRequest.class);
|
||||||
|
@ -379,7 +391,7 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
|
|
||||||
Settings invalidSettings = Settings.builder().put("xpack.notification.reporting.retries", -10).build();
|
Settings invalidSettings = Settings.builder().put("xpack.notification.reporting.retries", -10).build();
|
||||||
e = expectThrows(IllegalArgumentException.class,
|
e = expectThrows(IllegalArgumentException.class,
|
||||||
() -> new ReportingAttachmentParser(invalidSettings, httpClient, templateEngine));
|
() -> new ReportingAttachmentParser(invalidSettings, httpClient, templateEngine, clusterSettings));
|
||||||
assertThat(e.getMessage(), is("Failed to parse value [-10] for setting [xpack.notification.reporting.retries] must be >= 0"));
|
assertThat(e.getMessage(), is("Failed to parse value [-10] for setting [xpack.notification.reporting.retries] must be >= 0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,13 +417,161 @@ public class ReportingAttachmentParserTests extends ESTestCase {
|
||||||
requestCaptor.getAllValues().forEach(req -> assertThat(req.proxy(), is(proxy)));
|
requestCaptor.getAllValues().forEach(req -> assertThat(req.proxy(), is(proxy)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDefaultWarnings() throws Exception {
|
||||||
|
String content = randomAlphaOfLength(200);
|
||||||
|
String path = "/ovb/api/reporting/jobs/download/iu5zfzvk15oa8990bfas9wy2";
|
||||||
|
String randomContentType = randomAlphaOfLength(20);
|
||||||
|
String reportId = randomAlphaOfLength(5);
|
||||||
|
Map<String, String[]> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", new String[] { randomContentType });
|
||||||
|
WARNINGS.keySet().forEach((k) -> headers.put(k, new String[]{"true"}));
|
||||||
|
when(httpClient.execute(any(HttpRequest.class)))
|
||||||
|
.thenReturn(new HttpResponse(200, "{\"path\":\""+ path +"\", \"other\":\"content\"}"))
|
||||||
|
.thenReturn(new HttpResponse(200, content, headers));
|
||||||
|
|
||||||
|
ReportingAttachment reportingAttachment =
|
||||||
|
new ReportingAttachment(reportId, dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
||||||
|
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
||||||
|
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
||||||
|
assertThat(attachment.getWarnings(), hasSize(WARNINGS.keySet().size()));
|
||||||
|
//parameterize the messages
|
||||||
|
assertEquals(attachment.getWarnings(), WARNINGS.values().stream().
|
||||||
|
map(s -> String.format(Locale.ROOT, s, reportId)).collect(Collectors.toSet()));
|
||||||
|
|
||||||
|
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
||||||
|
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
||||||
|
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomWarningsNoParams() throws Exception {
|
||||||
|
String content = randomAlphaOfLength(200);
|
||||||
|
String path = "/ovb/api/reporting/jobs/download/iu5zfzvk15oa8990bfas9wy2";
|
||||||
|
String randomContentType = randomAlphaOfLength(20);
|
||||||
|
String reportId = randomAlphaOfLength(5);
|
||||||
|
Map<String, String[]> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", new String[] { randomContentType });
|
||||||
|
Map<String, String> customWarnings = new HashMap<>(WARNINGS.size());
|
||||||
|
WARNINGS.keySet().forEach((k) ->
|
||||||
|
{
|
||||||
|
final String warning = randomAlphaOfLength(20);
|
||||||
|
customWarnings.put(k, warning);
|
||||||
|
reportingAttachmentParser.addWarningText(k, warning);
|
||||||
|
headers.put(k, new String[]{"true"});
|
||||||
|
|
||||||
|
});
|
||||||
|
when(httpClient.execute(any(HttpRequest.class)))
|
||||||
|
.thenReturn(new HttpResponse(200, "{\"path\":\""+ path +"\", \"other\":\"content\"}"))
|
||||||
|
.thenReturn(new HttpResponse(200, content, headers));
|
||||||
|
|
||||||
|
ReportingAttachment reportingAttachment =
|
||||||
|
new ReportingAttachment(reportId, dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
||||||
|
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
||||||
|
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
||||||
|
assertThat(attachment.getWarnings(), hasSize(WARNINGS.keySet().size()));
|
||||||
|
assertEquals(attachment.getWarnings(), new HashSet<>(customWarnings.values()));
|
||||||
|
|
||||||
|
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
||||||
|
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
||||||
|
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCustomWarningsWithParams() throws Exception {
|
||||||
|
String content = randomAlphaOfLength(200);
|
||||||
|
String path = "/ovb/api/reporting/jobs/download/iu5zfzvk15oa8990bfas9wy2";
|
||||||
|
String randomContentType = randomAlphaOfLength(20);
|
||||||
|
String reportId = randomAlphaOfLength(5);
|
||||||
|
Map<String, String[]> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", new String[]{randomContentType});
|
||||||
|
Map<String, String> customWarnings = new HashMap<>(WARNINGS.size());
|
||||||
|
WARNINGS.keySet().forEach((k) ->
|
||||||
|
{
|
||||||
|
//add a parameter
|
||||||
|
final String warning = randomAlphaOfLength(20) + " %s";
|
||||||
|
customWarnings.put(k, warning);
|
||||||
|
reportingAttachmentParser.addWarningText(k, warning);
|
||||||
|
headers.put(k, new String[]{"true"});
|
||||||
|
|
||||||
|
});
|
||||||
|
when(httpClient.execute(any(HttpRequest.class)))
|
||||||
|
.thenReturn(new HttpResponse(200, "{\"path\":\"" + path + "\", \"other\":\"content\"}"))
|
||||||
|
.thenReturn(new HttpResponse(200, content, headers));
|
||||||
|
|
||||||
|
ReportingAttachment reportingAttachment =
|
||||||
|
new ReportingAttachment(reportId, dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
||||||
|
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
||||||
|
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
||||||
|
assertThat(attachment.getWarnings(), hasSize(WARNINGS.keySet().size()));
|
||||||
|
//parameterize the messages
|
||||||
|
assertEquals(attachment.getWarnings(), customWarnings.values().stream().
|
||||||
|
map(s -> String.format(Locale.ROOT, s, reportId)).collect(Collectors.toSet()));
|
||||||
|
//ensure the reportId is parameterized in
|
||||||
|
attachment.getWarnings().forEach(s -> {
|
||||||
|
assertThat(s, containsString(reportId));
|
||||||
|
});
|
||||||
|
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
||||||
|
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
||||||
|
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWarningsSuppress() throws Exception {
|
||||||
|
String content = randomAlphaOfLength(200);
|
||||||
|
String path = "/ovb/api/reporting/jobs/download/iu5zfzvk15oa8990bfas9wy2";
|
||||||
|
String randomContentType = randomAlphaOfLength(20);
|
||||||
|
String reportId = randomAlphaOfLength(5);
|
||||||
|
Map<String, String[]> headers = new HashMap<>();
|
||||||
|
headers.put("Content-Type", new String[]{randomContentType});
|
||||||
|
Map<String, String> customWarnings = new HashMap<>(WARNINGS.size());
|
||||||
|
WARNINGS.keySet().forEach((k) ->
|
||||||
|
{
|
||||||
|
final String warning = randomAlphaOfLength(20);
|
||||||
|
customWarnings.put(k, warning);
|
||||||
|
reportingAttachmentParser.addWarningText(k, warning);
|
||||||
|
reportingAttachmentParser.setWarningEnabled(false);
|
||||||
|
headers.put(k, new String[]{"true"});
|
||||||
|
|
||||||
|
});
|
||||||
|
when(httpClient.execute(any(HttpRequest.class)))
|
||||||
|
.thenReturn(new HttpResponse(200, "{\"path\":\"" + path + "\", \"other\":\"content\"}"))
|
||||||
|
.thenReturn(new HttpResponse(200, content, headers));
|
||||||
|
|
||||||
|
ReportingAttachment reportingAttachment =
|
||||||
|
new ReportingAttachment(reportId, dashboardUrl, randomBoolean(), TimeValue.timeValueMillis(1), 10, null, null);
|
||||||
|
Attachment attachment = reportingAttachmentParser.toAttachment(createWatchExecutionContext(), Payload.EMPTY, reportingAttachment);
|
||||||
|
assertThat(attachment, instanceOf(Attachment.Bytes.class));
|
||||||
|
assertThat(attachment.getWarnings(), hasSize(0));
|
||||||
|
|
||||||
|
Attachment.Bytes bytesAttachment = (Attachment.Bytes) attachment;
|
||||||
|
assertThat(new String(bytesAttachment.bytes(), StandardCharsets.UTF_8), is(content));
|
||||||
|
assertThat(bytesAttachment.contentType(), is(randomContentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWarningValidation() {
|
||||||
|
WARNINGS.forEach((k, v) -> {
|
||||||
|
String keyName = randomAlphaOfLength(5) + "notavalidsettingname";
|
||||||
|
IllegalArgumentException expectedException = expectThrows(IllegalArgumentException.class,
|
||||||
|
() -> reportingAttachmentParser.warningValidator(keyName, randomAlphaOfLength(10)));
|
||||||
|
assertThat(expectedException.getMessage(), containsString(keyName));
|
||||||
|
assertThat(expectedException.getMessage(), containsString("is not supported"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private WatchExecutionContext createWatchExecutionContext() {
|
private WatchExecutionContext createWatchExecutionContext() {
|
||||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
|
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
|
||||||
return mockExecutionContextBuilder("watch1")
|
return mockExecutionContextBuilder("watch1")
|
||||||
.wid(new Wid(randomAlphaOfLength(5), now))
|
.wid(new Wid(randomAlphaOfLength(5), now))
|
||||||
.payload(new Payload.Simple())
|
.payload(new Payload.Simple())
|
||||||
.time("watch1", now)
|
.time("watch1", now)
|
||||||
.metadata(Collections.emptyMap())
|
.metadata(Collections.emptyMap())
|
||||||
.buildMock();
|
.buildMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ClusterService mockClusterService() {
|
||||||
|
ClusterService clusterService = mock(ClusterService.class);
|
||||||
|
ClusterSettings clusterSettings =
|
||||||
|
new ClusterSettings(Settings.EMPTY,
|
||||||
|
ImmutableSet.of(INTERVAL_SETTING, RETRIES_SETTING, REPORT_WARNING_ENABLED_SETTING, REPORT_WARNING_TEXT));
|
||||||
|
when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
|
||||||
|
return clusterService;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue