Watcher: Allow for external email attachments

This feature is mainly done for the integration with the commercial reporting, but can be used
for anything else as well.

This adds a `attachments` to the email configuration, which can be used like this

```
"attachments" : {
  "some_id" : {
    "http" : {
      "request" : {
        "url" : "http://example.org/foo.pdf"
      }
    }
  },
  "other_id" : {
    "data" : {
      "format" : "json"
    }
  }
}
```

The main reason to pick this format is extensibility. If we would like to support another
attachment type, like an file reader, we could do so easily from an API point of view.

Closes elastic/elasticsearch#870

Original commit: elastic/x-pack-elasticsearch@66d14be965
This commit is contained in:
Alexander Reelsen 2016-01-07 10:40:52 +01:00
parent 2bab66dcb5
commit 75ab4f9470
19 changed files with 1207 additions and 22 deletions

View File

@ -0,0 +1,220 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.messy.tests;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.QueueDispatcher;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.script.mustache.MustachePlugin;
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
import org.elasticsearch.watcher.actions.email.service.attachment.DataAttachment;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachments;
import org.elasticsearch.watcher.actions.email.service.attachment.HttpRequestAttachment;
import org.elasticsearch.watcher.actions.email.service.support.EmailServer;
import org.elasticsearch.watcher.client.WatchSourceBuilder;
import org.elasticsearch.watcher.client.WatcherClient;
import org.elasticsearch.watcher.condition.compare.CompareCondition;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import org.elasticsearch.watcher.support.http.Scheme;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.watcher.trigger.schedule.IntervalSchedule;
import org.junit.After;
import org.junit.Before;
import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.MimeMessage;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.watcher.actions.ActionBuilders.emailAction;
import static org.elasticsearch.watcher.actions.email.DataAttachment.JSON;
import static org.elasticsearch.watcher.actions.email.DataAttachment.YAML;
import static org.elasticsearch.watcher.client.WatchSourceBuilders.watchBuilder;
import static org.elasticsearch.watcher.condition.ConditionBuilders.compareCondition;
import static org.elasticsearch.watcher.input.InputBuilders.searchInput;
import static org.elasticsearch.watcher.test.WatcherTestUtils.newInputSearchRequest;
import static org.elasticsearch.watcher.trigger.TriggerBuilders.schedule;
import static org.elasticsearch.watcher.trigger.schedule.Schedules.interval;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.startsWith;
public class EmailAttachmentTests extends AbstractWatcherIntegrationTestCase {
static final String USERNAME = "_user";
static final String PASSWORD = "_passwd";
private MockWebServer webServer = new MockWebServer();;
private EmailServer server;
@Before
public void startWebservice() throws Exception {
QueueDispatcher dispatcher = new QueueDispatcher();
dispatcher.setFailFast(true);
webServer.setDispatcher(dispatcher);
webServer.start(0);
MockResponse mockResponse = new MockResponse().setResponseCode(200)
.addHeader("Content-Type", "application/foo").setBody("This is the content");
webServer.enqueue(mockResponse);
}
@After
public void cleanup() throws Exception {
server.stop();
webServer.shutdown();
}
@Override
protected List<Class<? extends Plugin>> pluginTypes() {
List<Class<? extends Plugin>> types = new ArrayList<>();
types.addAll(super.pluginTypes());
types.add(MustachePlugin.class);
return types;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
if(server == null) {
//Need to construct the Email Server here as this happens before init()
server = EmailServer.localhost("2500-2600", USERNAME, PASSWORD, logger);
}
return Settings.builder()
.put(super.nodeSettings(nodeOrdinal))
.put("watcher.actions.email.service.account.test.smtp.auth", true)
.put("watcher.actions.email.service.account.test.smtp.user", USERNAME)
.put("watcher.actions.email.service.account.test.smtp.password", PASSWORD)
.put("watcher.actions.email.service.account.test.smtp.port", server.port())
.put("watcher.actions.email.service.account.test.smtp.host", "localhost")
.build();
}
public List<String> getAttachments(MimeMessage message) throws Exception {
Object content = message.getContent();
if (content instanceof String)
return null;
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
List<String> result = new ArrayList<>();
for (int i = 0; i < multipart.getCount(); i++) {
result.addAll(getAttachments(multipart.getBodyPart(i)));
}
return result;
}
return null;
}
private List<String> getAttachments(BodyPart part) throws Exception {
List<String> result = new ArrayList<>();
Object content = part.getContent();
if (content instanceof InputStream || content instanceof String) {
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) || Strings.hasLength(part.getFileName())) {
result.add(Streams.copyToString(new InputStreamReader(part.getInputStream(), StandardCharsets.UTF_8)));
return result;
} else {
return new ArrayList<>();
}
}
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
result.addAll(getAttachments(bodyPart));
}
}
return result;
}
public void testThatEmailAttachmentsAreSent() throws Exception {
org.elasticsearch.watcher.actions.email.DataAttachment dataFormat = randomFrom(JSON, YAML);
final CountDownLatch latch = new CountDownLatch(1);
server.addListener(new EmailServer.Listener() {
@Override
public void on(MimeMessage message) throws Exception {
assertThat(message.getSubject(), equalTo("Subject"));
List<String> attachments = getAttachments(message);
if (dataFormat == YAML) {
assertThat(attachments, hasItem(allOf(startsWith("---"), containsString("_test_id"))));
} else {
assertThat(attachments, hasItem(allOf(startsWith("{"), containsString("_test_id"))));
}
assertThat(attachments, hasItem(containsString("This is the content")));
latch.countDown();
}
});
WatcherClient watcherClient = watcherClient();
createIndex("idx");
// Have a sample document in the index, the watch is going to evaluate
client().prepareIndex("idx", "type").setSource("field", "value").get();
refresh();
SearchRequest searchRequest = newInputSearchRequest("idx").source(searchSource().query(matchAllQuery()));
List<EmailAttachmentParser.EmailAttachment> attachments = new ArrayList<>();
DataAttachment dataAttachment = DataAttachment.builder("my-id").dataAttachment(dataFormat).build();
attachments.add(dataAttachment);
HttpRequestTemplate requestTemplate = HttpRequestTemplate.builder("localhost", webServer.getPort()).path("/").scheme(Scheme.HTTP).build();
HttpRequestAttachment httpRequestAttachment = HttpRequestAttachment.builder("other-id").httpRequestTemplate(requestTemplate).build();
attachments.add(httpRequestAttachment);
EmailAttachments emailAttachments = new EmailAttachments(attachments);
XContentBuilder tmpBuilder = jsonBuilder();
emailAttachments.toXContent(tmpBuilder, ToXContent.EMPTY_PARAMS);
logger.info("TMP BUILDER {}", tmpBuilder.string());
EmailTemplate.Builder emailBuilder = EmailTemplate.builder().from("_from").to("_to").subject("Subject");
WatchSourceBuilder watchSourceBuilder = watchBuilder()
.trigger(schedule(interval(5, IntervalSchedule.Interval.Unit.SECONDS)))
.input(searchInput(searchRequest))
.condition(compareCondition("ctx.payload.hits.total", CompareCondition.Op.GT, 0l))
.addAction("_email", emailAction(emailBuilder).setAuthentication(USERNAME, PASSWORD.toCharArray())
.setAttachments(emailAttachments));
logger.info("TMP WATCHSOURCE {}", watchSourceBuilder.build().getBytes().toUtf8());
watcherClient.preparePutWatch("_test_id")
.setSource(watchSourceBuilder)
.get();
if (timeWarped()) {
timeWarp().scheduler().trigger("_test_id");
refresh();
}
assertWatchWithMinimumPerformedActionsCount("_test_id", 1);
if (!latch.await(5, TimeUnit.SECONDS)) {
fail("waited too long for email to be received");
}
}
}

View File

@ -12,6 +12,10 @@ import org.elasticsearch.watcher.actions.email.EmailActionFactory;
import org.elasticsearch.watcher.actions.email.service.EmailService; import org.elasticsearch.watcher.actions.email.service.EmailService;
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer; import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
import org.elasticsearch.watcher.actions.email.service.InternalEmailService; import org.elasticsearch.watcher.actions.email.service.InternalEmailService;
import org.elasticsearch.watcher.actions.email.service.attachment.DataAttachmentParser;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentsParser;
import org.elasticsearch.watcher.actions.email.service.attachment.HttpEmailAttachementParser;
import org.elasticsearch.watcher.actions.hipchat.HipChatAction; import org.elasticsearch.watcher.actions.hipchat.HipChatAction;
import org.elasticsearch.watcher.actions.hipchat.HipChatActionFactory; import org.elasticsearch.watcher.actions.hipchat.HipChatActionFactory;
import org.elasticsearch.watcher.actions.hipchat.service.HipChatService; import org.elasticsearch.watcher.actions.hipchat.service.HipChatService;
@ -39,6 +43,7 @@ import java.util.Map;
public class WatcherActionModule extends AbstractModule { public class WatcherActionModule extends AbstractModule {
private final Map<String, Class<? extends ActionFactory>> parsers = new HashMap<>(); private final Map<String, Class<? extends ActionFactory>> parsers = new HashMap<>();
private final Map<String, Class<? extends EmailAttachmentParser>> emailAttachmentParsers = new HashMap<>();
public WatcherActionModule() { public WatcherActionModule() {
registerAction(EmailAction.TYPE, EmailActionFactory.class); registerAction(EmailAction.TYPE, EmailActionFactory.class);
@ -48,12 +53,19 @@ public class WatcherActionModule extends AbstractModule {
registerAction(HipChatAction.TYPE, HipChatActionFactory.class); registerAction(HipChatAction.TYPE, HipChatActionFactory.class);
registerAction(SlackAction.TYPE, SlackActionFactory.class); registerAction(SlackAction.TYPE, SlackActionFactory.class);
registerAction(PagerDutyAction.TYPE, PagerDutyActionFactory.class); registerAction(PagerDutyAction.TYPE, PagerDutyActionFactory.class);
registerEmailAttachmentParser(HttpEmailAttachementParser.TYPE, HttpEmailAttachementParser.class);
registerEmailAttachmentParser(DataAttachmentParser.TYPE, DataAttachmentParser.class);
} }
public void registerAction(String type, Class<? extends ActionFactory> parserType) { public void registerAction(String type, Class<? extends ActionFactory> parserType) {
parsers.put(type, parserType); parsers.put(type, parserType);
} }
public void registerEmailAttachmentParser(String type, Class<? extends EmailAttachmentParser> parserClass) {
emailAttachmentParsers.put(type, parserClass);
}
@Override @Override
protected void configure() { protected void configure() {
@ -70,6 +82,12 @@ public class WatcherActionModule extends AbstractModule {
bind(InternalEmailService.class).asEagerSingleton(); bind(InternalEmailService.class).asEagerSingleton();
bind(EmailService.class).to(InternalEmailService.class).asEagerSingleton(); bind(EmailService.class).to(InternalEmailService.class).asEagerSingleton();
MapBinder<String, EmailAttachmentParser> emailParsersBinder = MapBinder.newMapBinder(binder(), String.class, EmailAttachmentParser.class);
for (Map.Entry<String, Class<? extends EmailAttachmentParser>> entry : emailAttachmentParsers.entrySet()) {
emailParsersBinder.addBinding(entry.getKey()).to(entry.getValue()).asEagerSingleton();
}
bind(EmailAttachmentsParser.class).asEagerSingleton();
// hipchat // hipchat
bind(InternalHipChatService.class).asEagerSingleton(); bind(InternalHipChatService.class).asEagerSingleton();
bind(HipChatService.class).to(InternalHipChatService.class); bind(HipChatService.class).to(InternalHipChatService.class);

View File

@ -16,6 +16,8 @@ import org.elasticsearch.watcher.actions.email.service.Authentication;
import org.elasticsearch.watcher.actions.email.service.Email; import org.elasticsearch.watcher.actions.email.service.Email;
import org.elasticsearch.watcher.actions.email.service.EmailTemplate; import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
import org.elasticsearch.watcher.actions.email.service.Profile; import org.elasticsearch.watcher.actions.email.service.Profile;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachments;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentsParser;
import org.elasticsearch.watcher.support.secret.Secret; import org.elasticsearch.watcher.support.secret.Secret;
import org.elasticsearch.watcher.support.xcontent.WatcherParams; import org.elasticsearch.watcher.support.xcontent.WatcherParams;
import org.elasticsearch.watcher.support.xcontent.WatcherXContentParser; import org.elasticsearch.watcher.support.xcontent.WatcherXContentParser;
@ -35,13 +37,15 @@ public class EmailAction implements Action {
private final @Nullable Authentication auth; private final @Nullable Authentication auth;
private final @Nullable Profile profile; private final @Nullable Profile profile;
private final @Nullable DataAttachment dataAttachment; private final @Nullable DataAttachment dataAttachment;
private final @Nullable EmailAttachments emailAttachments;
public EmailAction(EmailTemplate email, @Nullable String account, @Nullable Authentication auth, @Nullable Profile profile, @Nullable DataAttachment dataAttachment) { public EmailAction(EmailTemplate email, @Nullable String account, @Nullable Authentication auth, @Nullable Profile profile, @Nullable DataAttachment dataAttachment, @Nullable EmailAttachments emailAttachments) {
this.email = email; this.email = email;
this.account = account; this.account = account;
this.auth = auth; this.auth = auth;
this.profile = profile; this.profile = profile;
this.dataAttachment = dataAttachment; this.dataAttachment = dataAttachment;
this.emailAttachments = emailAttachments;
} }
public EmailTemplate getEmail() { public EmailTemplate getEmail() {
@ -64,6 +68,10 @@ public class EmailAction implements Action {
return dataAttachment; return dataAttachment;
} }
public EmailAttachments getAttachments() {
return emailAttachments;
}
@Override @Override
public String type() { public String type() {
return TYPE; return TYPE;
@ -80,6 +88,7 @@ public class EmailAction implements Action {
if (account != null ? !account.equals(action.account) : action.account != null) return false; if (account != null ? !account.equals(action.account) : action.account != null) return false;
if (auth != null ? !auth.equals(action.auth) : action.auth != null) return false; if (auth != null ? !auth.equals(action.auth) : action.auth != null) return false;
if (profile != action.profile) return false; if (profile != action.profile) return false;
if (emailAttachments != null && !emailAttachments.equals(action.emailAttachments)) return false;
return !(dataAttachment != null ? !dataAttachment.equals(action.dataAttachment) : action.dataAttachment != null); return !(dataAttachment != null ? !dataAttachment.equals(action.dataAttachment) : action.dataAttachment != null);
} }
@ -90,6 +99,7 @@ public class EmailAction implements Action {
result = 31 * result + (auth != null ? auth.hashCode() : 0); result = 31 * result + (auth != null ? auth.hashCode() : 0);
result = 31 * result + (profile != null ? profile.hashCode() : 0); result = 31 * result + (profile != null ? profile.hashCode() : 0);
result = 31 * result + (dataAttachment != null ? dataAttachment.hashCode() : 0); result = 31 * result + (dataAttachment != null ? dataAttachment.hashCode() : 0);
result = 31 * result + (emailAttachments != null ? emailAttachments.hashCode() : 0);
return result; return result;
} }
@ -111,17 +121,21 @@ public class EmailAction implements Action {
if (dataAttachment != null) { if (dataAttachment != null) {
builder.field(Field.ATTACH_DATA.getPreferredName(), dataAttachment, params); builder.field(Field.ATTACH_DATA.getPreferredName(), dataAttachment, params);
} }
if (emailAttachments != null) {
emailAttachments.toXContent(builder, params);
}
email.xContentBody(builder, params); email.xContentBody(builder, params);
return builder.endObject(); return builder.endObject();
} }
public static EmailAction parse(String watchId, String actionId, XContentParser parser) throws IOException { public static EmailAction parse(String watchId, String actionId, XContentParser parser, EmailAttachmentsParser emailAttachmentsParser) throws IOException {
EmailTemplate.Parser emailParser = new EmailTemplate.Parser(); EmailTemplate.Parser emailParser = new EmailTemplate.Parser();
String account = null; String account = null;
String user = null; String user = null;
Secret password = null; Secret password = null;
Profile profile = Profile.STANDARD; Profile profile = Profile.STANDARD;
DataAttachment dataAttachment = null; DataAttachment dataAttachment = null;
EmailAttachments attachments = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token; XContentParser.Token token;
@ -134,6 +148,8 @@ public class EmailAction implements Action {
} catch (IOException ioe) { } catch (IOException ioe) {
throw new ElasticsearchParseException("could not parse [{}] action [{}/{}]. failed to parse data attachment field [{}]", ioe, TYPE, watchId, actionId, currentFieldName); throw new ElasticsearchParseException("could not parse [{}] action [{}/{}]. failed to parse data attachment field [{}]", ioe, TYPE, watchId, actionId, currentFieldName);
} }
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.ATTACHMENTS)) {
attachments = emailAttachmentsParser.parse(parser);
} else if (!emailParser.handle(currentFieldName, parser)) { } else if (!emailParser.handle(currentFieldName, parser)) {
if (token == XContentParser.Token.VALUE_STRING) { if (token == XContentParser.Token.VALUE_STRING) {
if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.ACCOUNT)) { if (ParseFieldMatcher.STRICT.match(currentFieldName, Field.ACCOUNT)) {
@ -162,7 +178,7 @@ public class EmailAction implements Action {
auth = new Authentication(user, password); auth = new Authentication(user, password);
} }
return new EmailAction(emailParser.parsedTemplate(), account, auth, profile, dataAttachment); return new EmailAction(emailParser.parsedTemplate(), account, auth, profile, dataAttachment, attachments);
} }
public static Builder builder(EmailTemplate email) { public static Builder builder(EmailTemplate email) {
@ -232,6 +248,7 @@ public class EmailAction implements Action {
@Nullable Authentication auth; @Nullable Authentication auth;
@Nullable Profile profile; @Nullable Profile profile;
@Nullable DataAttachment dataAttachment; @Nullable DataAttachment dataAttachment;
@Nullable EmailAttachments attachments;
private Builder(EmailTemplate email) { private Builder(EmailTemplate email) {
this.email = email; this.email = email;
@ -252,13 +269,19 @@ public class EmailAction implements Action {
return this; return this;
} }
@Deprecated
public Builder setAttachPayload(DataAttachment dataAttachment) { public Builder setAttachPayload(DataAttachment dataAttachment) {
this.dataAttachment = dataAttachment; this.dataAttachment = dataAttachment;
return this; return this;
} }
public Builder setAttachments(EmailAttachments attachments) {
this.attachments = attachments;
return this;
}
public EmailAction build() { public EmailAction build() {
return new EmailAction(email, account, auth, profile, dataAttachment); return new EmailAction(email, account, auth, profile, dataAttachment, attachments);
} }
} }
@ -272,6 +295,7 @@ public class EmailAction implements Action {
ParseField USER = new ParseField("user"); ParseField USER = new ParseField("user");
ParseField PASSWORD = new ParseField("password"); ParseField PASSWORD = new ParseField("password");
ParseField ATTACH_DATA = new ParseField("attach_data"); ParseField ATTACH_DATA = new ParseField("attach_data");
ParseField ATTACHMENTS = new ParseField("attachments");
// result fields // result fields
ParseField MESSAGE = new ParseField("message"); ParseField MESSAGE = new ParseField("message");

View File

@ -12,9 +12,12 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.actions.ActionFactory; import org.elasticsearch.watcher.actions.ActionFactory;
import org.elasticsearch.watcher.actions.email.service.EmailService; import org.elasticsearch.watcher.actions.email.service.EmailService;
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer; import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentsParser;
import org.elasticsearch.watcher.support.text.TextTemplateEngine; import org.elasticsearch.watcher.support.text.TextTemplateEngine;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/** /**
* *
@ -24,13 +27,18 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
private final EmailService emailService; private final EmailService emailService;
private final TextTemplateEngine templateEngine; private final TextTemplateEngine templateEngine;
private final HtmlSanitizer htmlSanitizer; private final HtmlSanitizer htmlSanitizer;
private final EmailAttachmentsParser emailAttachmentsParser;
private final Map<String, EmailAttachmentParser> emailAttachmentParsers;
@Inject @Inject
public EmailActionFactory(Settings settings, EmailService emailService, TextTemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) { public EmailActionFactory(Settings settings, EmailService emailService, TextTemplateEngine templateEngine, HtmlSanitizer htmlSanitizer,
EmailAttachmentsParser emailAttachmentsParser, Map<String, EmailAttachmentParser> emailAttachmentParsers) {
super(Loggers.getLogger(ExecutableEmailAction.class, settings)); super(Loggers.getLogger(ExecutableEmailAction.class, settings));
this.emailService = emailService; this.emailService = emailService;
this.templateEngine = templateEngine; this.templateEngine = templateEngine;
this.htmlSanitizer = htmlSanitizer; this.htmlSanitizer = htmlSanitizer;
this.emailAttachmentsParser = emailAttachmentsParser;
this.emailAttachmentParsers = emailAttachmentParsers;
} }
@Override @Override
@ -40,11 +48,11 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
@Override @Override
public EmailAction parseAction(String watchId, String actionId, XContentParser parser) throws IOException { public EmailAction parseAction(String watchId, String actionId, XContentParser parser) throws IOException {
return EmailAction.parse(watchId, actionId, parser); return EmailAction.parse(watchId, actionId, parser, emailAttachmentsParser);
} }
@Override @Override
public ExecutableEmailAction createExecutable(EmailAction action) { public ExecutableEmailAction createExecutable(EmailAction action) {
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine, htmlSanitizer); return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine, htmlSanitizer, emailAttachmentParsers);
} }
} }

View File

@ -12,6 +12,7 @@ import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.actions.email.service.Email; import org.elasticsearch.watcher.actions.email.service.Email;
import org.elasticsearch.watcher.actions.email.service.EmailService; import org.elasticsearch.watcher.actions.email.service.EmailService;
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer; import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
import org.elasticsearch.watcher.execution.WatchExecutionContext; import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.Variables; import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.text.TextTemplateEngine; import org.elasticsearch.watcher.support.text.TextTemplateEngine;
@ -27,12 +28,15 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
final EmailService emailService; final EmailService emailService;
final TextTemplateEngine templateEngine; final TextTemplateEngine templateEngine;
final HtmlSanitizer htmlSanitizer; final HtmlSanitizer htmlSanitizer;
private final Map<String, EmailAttachmentParser> emailAttachmentParsers;
public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TextTemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) { public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TextTemplateEngine templateEngine, HtmlSanitizer htmlSanitizer,
Map<String, EmailAttachmentParser> emailAttachmentParsers) {
super(action, logger); super(action, logger);
this.emailService = emailService; this.emailService = emailService;
this.templateEngine = templateEngine; this.templateEngine = templateEngine;
this.htmlSanitizer = htmlSanitizer; this.htmlSanitizer = htmlSanitizer;
this.emailAttachmentParsers = emailAttachmentParsers;
} }
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception { public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
@ -45,6 +49,14 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
attachments.put(attachment.id(), attachment); attachments.put(attachment.id(), attachment);
} }
if (action.getAttachments() != null && action.getAttachments().getAttachments().size() > 0) {
for (EmailAttachmentParser.EmailAttachment emailAttachment : action.getAttachments().getAttachments()) {
EmailAttachmentParser parser = emailAttachmentParsers.get(emailAttachment.type());
Attachment attachment = parser.toAttachment(ctx, payload, emailAttachment);
attachments.put(attachment.id(), attachment);
}
}
Email.Builder email = action.getEmail().render(templateEngine, model, htmlSanitizer, attachments); Email.Builder email = action.getEmail().render(templateEngine, model, htmlSanitizer, attachments);
email.id(ctx.id().value()); email.id(ctx.id().value());

View File

@ -0,0 +1,66 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
public class DataAttachment implements EmailAttachmentParser.EmailAttachment {
private final String id;
private final org.elasticsearch.watcher.actions.email.DataAttachment dataAttachment;
public DataAttachment(String id, org.elasticsearch.watcher.actions.email.DataAttachment dataAttachment) {
this.id = id;
this.dataAttachment = dataAttachment;
}
public org.elasticsearch.watcher.actions.email.DataAttachment getDataAttachment() {
return dataAttachment;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(id).startObject(DataAttachmentParser.TYPE);
if (dataAttachment == org.elasticsearch.watcher.actions.email.DataAttachment.YAML) {
builder.field("format", "yaml");
} else {
builder.field("format", "json");
}
return builder.endObject().endObject();
}
@Override
public String type() {
return DataAttachmentParser.TYPE;
}
public static Builder builder(String id) {
return new Builder(id);
}
public static class Builder {
private String id;
private org.elasticsearch.watcher.actions.email.DataAttachment dataAttachment;
private Builder(String id) {
this.id = id;
}
public Builder dataAttachment(org.elasticsearch.watcher.actions.email.DataAttachment dataAttachment) {
this.dataAttachment = dataAttachment;
return this;
}
public DataAttachment build() {
return new DataAttachment(id, dataAttachment);
}
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.watch.Payload;
import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.watcher.actions.email.DataAttachment.resolve;
public class DataAttachmentParser implements EmailAttachmentParser<DataAttachment> {
interface Fields {
ParseField FORMAT = new ParseField("format");
}
public static final String TYPE = "data";
@Override
public String type() {
return TYPE;
}
@Override
public DataAttachment parse(String id, XContentParser parser) throws IOException {
org.elasticsearch.watcher.actions.email.DataAttachment dataAttachment = org.elasticsearch.watcher.actions.email.DataAttachment.YAML;
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (Strings.hasLength(currentFieldName) && ParseFieldMatcher.STRICT.match(currentFieldName, Fields.FORMAT)) {
if (token == XContentParser.Token.VALUE_STRING) {
dataAttachment = resolve(parser.text());
} else {
throw new ElasticsearchParseException("could not parse data attachment. expected string value for [{}] field but found [{}] instead", currentFieldName, token);
}
}
}
return new DataAttachment(id, dataAttachment);
}
@Override
public Attachment toAttachment(WatchExecutionContext ctx, Payload payload, DataAttachment attachment) {
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
return attachment.getDataAttachment().create(model);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import java.io.IOException;
/**
* Marker interface for email attachments that have an additional execution step and are used by
* EmailAttachmentParser class
*/
public interface EmailAttachmentParser<T extends EmailAttachmentParser.EmailAttachment> {
interface EmailAttachment extends ToXContent {
/**
* @return A type to identify the email attachment, same as the parser identifier
*/
String type();
}
/**
* @return An identifier of this parser
*/
String type();
/**
* A parser to create an EmailAttachment, that is serializable and does not execute anything
*
* @param id The id of this attachment, parsed from the outer content
* @param parser The XContentParser used for parsing
* @return A concrete EmailAttachment
* @throws IOException in case parsing fails
*/
T parse(String id, XContentParser parser) throws IOException;
/**
* Converts an email attachment to an attachment, potentially executing code like an HTTP request
* @param context The WatchExecutionContext supplied with the whole watch execution
* @param payload The Payload supplied with the action
* @param attachment The typed attachment
* @return An attachment that is ready to be used in a MimeMessage
*/
Attachment toAttachment(WatchExecutionContext context, Payload payload, T attachment);
}

View File

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.List;
public class EmailAttachments implements ToXContent {
public interface Fields {
ParseField ATTACHMENTS = new ParseField("attachments");
}
private final List<EmailAttachmentParser.EmailAttachment> attachments;
public EmailAttachments(List<EmailAttachmentParser.EmailAttachment> attachments) {
this.attachments = attachments;
}
public List<EmailAttachmentParser.EmailAttachment> getAttachments() {
return attachments;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (attachments != null && attachments.size() > 0) {
builder.startObject(Fields.ATTACHMENTS.getPreferredName());
for (EmailAttachmentParser.EmailAttachment attachment : attachments) {
attachment.toXContent(builder, params);
}
builder.endObject();
}
return builder;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class EmailAttachmentsParser {
private Map<String, EmailAttachmentParser> parsers;
@Inject
public EmailAttachmentsParser(Map<String, EmailAttachmentParser> parsers) {
this.parsers = parsers;
}
public EmailAttachments parse(XContentParser parser) throws IOException {
List<EmailAttachmentParser.EmailAttachment> attachments = new ArrayList<>();
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else {
if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
String currentAttachmentType = null;
if (parser.nextToken() == XContentParser.Token.FIELD_NAME) {
currentAttachmentType = parser.currentName();
}
parser.nextToken();
EmailAttachmentParser emailAttachmentParser = parsers.get(currentAttachmentType);
if (emailAttachmentParser == null) {
throw new ElasticsearchParseException("Cannot parse attachment of type " + currentAttachmentType);
}
EmailAttachmentParser.EmailAttachment emailAttachment = emailAttachmentParser.parse(currentFieldName, parser);
attachments.add(emailAttachment);
// one further to skip the end_object from the attachment
parser.nextToken();
}
}
}
return new EmailAttachments(attachments);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.Variables;
import org.elasticsearch.watcher.support.http.HttpClient;
import org.elasticsearch.watcher.support.http.HttpRequest;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import org.elasticsearch.watcher.support.http.HttpResponse;
import org.elasticsearch.watcher.support.text.TextTemplateEngine;
import org.elasticsearch.watcher.watch.Payload;
import java.io.IOException;
import java.util.Map;
public class HttpEmailAttachementParser implements EmailAttachmentParser<HttpRequestAttachment> {
public interface Fields {
ParseField REQUEST = new ParseField("request");
ParseField CONTENT_TYPE = new ParseField("content_type");
}
public static final String TYPE = "http";
private final HttpClient httpClient;
private HttpRequestTemplate.Parser requestTemplateParser;
private final TextTemplateEngine templateEngine;
private final ESLogger logger;
@Inject
public HttpEmailAttachementParser(HttpClient httpClient, HttpRequestTemplate.Parser requestTemplateParser, TextTemplateEngine templateEngine) {
this.httpClient = httpClient;
this.requestTemplateParser = requestTemplateParser;
this.templateEngine = templateEngine;
this.logger = Loggers.getLogger(getClass());
}
@Override
public String type() {
return TYPE;
}
@Override
public HttpRequestAttachment parse(String id, XContentParser parser) throws IOException {
String contentType = null;
HttpRequestTemplate requestTemplate = null;
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.CONTENT_TYPE)) {
contentType = parser.text();
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.REQUEST)) {
requestTemplate = requestTemplateParser.parse(parser);
} else {
throw new ElasticsearchParseException("Unknown field name [" + currentFieldName + "] in http request attachment configuration");
}
}
if (requestTemplate != null) {
return new HttpRequestAttachment(id, requestTemplate, contentType);
}
throw new ElasticsearchParseException("Could not parse http request attachment");
}
@Override
public Attachment toAttachment(WatchExecutionContext context, Payload payload, HttpRequestAttachment attachment) {
Map<String, Object> model = Variables.createCtxModel(context, payload);
HttpRequest httpRequest = attachment.getRequestTemplate().render(templateEngine, model);
try {
HttpResponse response = httpClient.execute(httpRequest);
// check for status 200, only then append attachment
if (response.status() >= 200 && response.status() < 300) {
if (response.hasContent()) {
String contentType = attachment.getContentType();
String attachmentContentType = Strings.hasLength(contentType) ? contentType : response.contentType();
return new Attachment.Bytes(attachment.getId(), response.body().toBytes(), attachmentContentType);
} else {
logger.error("Empty response body: [host[{}], port[{}], method[{}], path[{}]: response status [{}]", httpRequest.host(),
httpRequest.port(), httpRequest.method(), httpRequest.path(), response.status());
}
} else {
logger.error("Error getting http response: [host[{}], port[{}], method[{}], path[{}]: response status [{}]", httpRequest.host(),
httpRequest.port(), httpRequest.method(), httpRequest.path(), response.status());
}
} catch (IOException e) {
logger.error("Error executing HTTP request: [host[{}], port[{}], method[{}], path[{}]: [{}]", e, httpRequest.port(),
httpRequest.method(), httpRequest.path(), e.getMessage());
}
return null;
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import java.io.IOException;
public class HttpRequestAttachment implements EmailAttachmentParser.EmailAttachment {
private final HttpRequestTemplate requestTemplate;
private final String contentType;
private String id;
public HttpRequestAttachment(String id, HttpRequestTemplate requestTemplate, @Nullable String contentType) {
this.id = id;
this.requestTemplate = requestTemplate;
this.contentType = contentType;
}
public HttpRequestTemplate getRequestTemplate() {
return requestTemplate;
}
public String getContentType() {
return contentType;
}
public String getId() {
return id;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(id)
.startObject(HttpEmailAttachementParser.TYPE)
.field(HttpEmailAttachementParser.Fields.REQUEST.getPreferredName(), requestTemplate, params);
if (Strings.hasLength(contentType)) {
builder.field(HttpEmailAttachementParser.Fields.CONTENT_TYPE.getPreferredName(), contentType);
}
return builder.endObject().endObject();
}
public static Builder builder(String id) {
return new Builder(id);
}
@Override
public String type() {
return HttpEmailAttachementParser.TYPE;
}
public static class Builder {
private String id;
private HttpRequestTemplate httpRequestTemplate;
private String contentType;
private Builder(String id) {
this.id = id;
}
public Builder httpRequestTemplate(HttpRequestTemplate httpRequestTemplate) {
this.httpRequestTemplate = httpRequestTemplate;
return this;
}
public Builder contentType(String contentType) {
this.contentType = contentType;
return this;
}
public HttpRequestAttachment build() {
return new HttpRequestAttachment(id, httpRequestTemplate, contentType);
}
}
}

View File

@ -5,9 +5,11 @@
*/ */
package org.elasticsearch.watcher.actions.email; package org.elasticsearch.watcher.actions.email;
import com.google.common.base.Charsets;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -15,23 +17,40 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.watcher.actions.Action; import org.elasticsearch.watcher.actions.Action;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.actions.email.service.Authentication; import org.elasticsearch.watcher.actions.email.service.Authentication;
import org.elasticsearch.watcher.actions.email.service.Email; import org.elasticsearch.watcher.actions.email.service.Email;
import org.elasticsearch.watcher.actions.email.service.EmailService; import org.elasticsearch.watcher.actions.email.service.EmailService;
import org.elasticsearch.watcher.actions.email.service.EmailTemplate; import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer; import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
import org.elasticsearch.watcher.actions.email.service.Profile; import org.elasticsearch.watcher.actions.email.service.Profile;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentParser;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachments;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentsParser;
import org.elasticsearch.watcher.actions.email.service.attachment.HttpEmailAttachementParser;
import org.elasticsearch.watcher.execution.WatchExecutionContext; import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.execution.Wid; import org.elasticsearch.watcher.execution.Wid;
import org.elasticsearch.watcher.support.http.HttpClient;
import org.elasticsearch.watcher.support.http.HttpRequest;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import org.elasticsearch.watcher.support.http.HttpResponse;
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.watcher.support.secret.Secret; import org.elasticsearch.watcher.support.secret.Secret;
import org.elasticsearch.watcher.support.secret.SecretService;
import org.elasticsearch.watcher.support.text.TextTemplate; import org.elasticsearch.watcher.support.text.TextTemplate;
import org.elasticsearch.watcher.support.text.TextTemplateEngine; import org.elasticsearch.watcher.support.text.TextTemplateEngine;
import org.elasticsearch.watcher.support.xcontent.WatcherParams; import org.elasticsearch.watcher.support.xcontent.WatcherParams;
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase; import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTestCase;
import org.elasticsearch.watcher.watch.Payload; import org.elasticsearch.watcher.watch.Payload;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.DateTimeZone; import org.joda.time.DateTimeZone;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -43,10 +62,12 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -54,6 +75,12 @@ import static org.mockito.Mockito.when;
* *
*/ */
public class EmailActionTests extends ESTestCase { public class EmailActionTests extends ESTestCase {
private SecretService secretService = mock(SecretService.class);
private HttpAuthRegistry registry = new HttpAuthRegistry(singletonMap("basic", new BasicAuthFactory(secretService)));
private HttpClient httpClient = mock(HttpClient.class);
private static final EmailAttachmentsParser EMPTY_EMAIL_ATTACHMENTS_PARSER = new EmailAttachmentsParser(Collections.emptyMap());
public void testExecute() throws Exception { public void testExecute() throws Exception {
final String account = "account1"; final String account = "account1";
EmailService service = new AbstractWatcherIntegrationTestCase.NoopEmailService() { EmailService service = new AbstractWatcherIntegrationTestCase.NoopEmailService() {
@ -92,9 +119,11 @@ public class EmailActionTests extends ESTestCase {
Profile profile = randomFrom(Profile.values()); Profile profile = randomFrom(Profile.values());
DataAttachment dataAttachment = randomDataAttachment(); DataAttachment dataAttachment = randomDataAttachment();
// TODO RANDOMIZE ME
EmailAttachments emailAttachments = new EmailAttachments(new ArrayList<>());
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment); EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment, emailAttachments);
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer); ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer, Collections.emptyMap());
Map<String, Object> data = new HashMap<>(); Map<String, Object> data = new HashMap<>();
Payload payload = new Payload.Simple(data); Payload payload = new Payload.Simple(data);
@ -253,7 +282,7 @@ public class EmailActionTests extends ESTestCase {
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
ExecutableEmailAction executable = new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer) ExecutableEmailAction executable = new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer, EMPTY_EMAIL_ATTACHMENTS_PARSER, Collections.emptyMap())
.parseExecutable(randomAsciiOfLength(8), randomAsciiOfLength(3), parser); .parseExecutable(randomAsciiOfLength(8), randomAsciiOfLength(3), parser);
assertThat(executable, notNullValue()); assertThat(executable, notNullValue());
@ -331,9 +360,11 @@ public class EmailActionTests extends ESTestCase {
Profile profile = randomFrom(Profile.values()); Profile profile = randomFrom(Profile.values());
String account = randomAsciiOfLength(6); String account = randomAsciiOfLength(6);
DataAttachment dataAttachment = randomDataAttachment(); DataAttachment dataAttachment = randomDataAttachment();
// TODO randomize
EmailAttachments emailAttachments = new EmailAttachments(new ArrayList<>());
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment); EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment, emailAttachments);
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer); ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer, Collections.emptyMap());
boolean hideSecrets = randomBoolean(); boolean hideSecrets = randomBoolean();
ToXContent.Params params = WatcherParams.builder().hideSecrets(hideSecrets).build(); ToXContent.Params params = WatcherParams.builder().hideSecrets(hideSecrets).build();
@ -344,7 +375,8 @@ public class EmailActionTests extends ESTestCase {
logger.info(bytes.toUtf8()); logger.info(bytes.toUtf8());
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes); XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
parser.nextToken(); parser.nextToken();
ExecutableEmailAction parsed = new EmailActionFactory(Settings.EMPTY, service, engine, htmlSanitizer)
ExecutableEmailAction parsed = new EmailActionFactory(Settings.EMPTY, service, engine, htmlSanitizer, EMPTY_EMAIL_ATTACHMENTS_PARSER, Collections.emptyMap())
.parseExecutable(randomAsciiOfLength(4), randomAsciiOfLength(10), parser); .parseExecutable(randomAsciiOfLength(4), randomAsciiOfLength(10), parser);
if (!hideSecrets) { if (!hideSecrets) {
@ -369,18 +401,100 @@ public class EmailActionTests extends ESTestCase {
EmailService emailService = mock(EmailService.class); EmailService emailService = mock(EmailService.class);
TextTemplateEngine engine = mock(TextTemplateEngine.class); TextTemplateEngine engine = mock(TextTemplateEngine.class);
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class); HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
EmailAttachmentsParser emailAttachmentsParser = mock(EmailAttachmentsParser.class);
XContentBuilder builder = jsonBuilder().startObject().field("unknown_field", "value"); XContentBuilder builder = jsonBuilder().startObject().field("unknown_field", "value");
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes()); XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
parser.nextToken(); parser.nextToken();
try { try {
new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer) new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer, emailAttachmentsParser, Collections.emptyMap())
.parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser); .parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser);
} catch (ElasticsearchParseException e) { } catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("unexpected string field [unknown_field]")); assertThat(e.getMessage(), containsString("unexpected string field [unknown_field]"));
} }
} }
public void testRequestAttachmentGetsAppendedToEmailAttachments() throws Exception {
String attachmentId = "my_attachment";
EmailService emailService = new AbstractWatcherIntegrationTestCase.NoopEmailService();
TextTemplateEngine engine = mock(TextTemplateEngine.class);
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
HttpClient httpClient = mock(HttpClient.class);
// setup mock response
Map<String, String[]> headers = new HashMap<>(1);
headers.put(HttpHeaders.Names.CONTENT_TYPE, new String[]{"plain/text"});
String content = "My wonderful text";
HttpResponse mockResponse = new HttpResponse(200, content, headers);
when(httpClient.execute(any(HttpRequest.class))).thenReturn(mockResponse);
// setup email parsers
HttpRequestTemplate.Parser httpRequestTemplateParser = new HttpRequestTemplate.Parser(registry);
Map<String, EmailAttachmentParser> attachmentParsers = new HashMap<>();
attachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, httpRequestTemplateParser, engine));
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
XContentBuilder builder = jsonBuilder().startObject()
.startObject("attachments")
.startObject(attachmentId)
.startObject("http")
.startObject("request")
.field("host", "localhost")
.field("port", 443)
.field("path", "/the/evil/test")
.endObject()
.endObject()
.endObject()
.endObject()
.endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
logger.info("JSON: {}", builder.string());
parser.nextToken();
ExecutableEmailAction executableEmailAction = new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer, emailAttachmentsParser, attachmentParsers)
.parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser);
DateTime now = DateTime.now(DateTimeZone.UTC);
Wid wid = new Wid(randomAsciiOfLength(5), randomLong(), now);
Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
WatchExecutionContext ctx = mockExecutionContextBuilder("watch1")
.wid(wid)
.payload(new Payload.Simple())
.time("watch1", now)
.metadata(metadata)
.buildMock();
Action.Result result = executableEmailAction.execute("test", ctx, new Payload.Simple());
assertThat(result, instanceOf(EmailAction.Result.Success.class));
EmailAction.Result.Success successResult = (EmailAction.Result.Success) result;
Map<String, Attachment> attachments = successResult.email().attachments();
assertThat(attachments.keySet(), hasSize(1));
assertThat(attachments, hasKey(attachmentId));
Attachment externalAttachment = attachments.get(attachmentId);
assertThat(externalAttachment.bodyPart(), is(notNullValue()));
InputStream is = externalAttachment.bodyPart().getInputStream();
String data = Streams.copyToString(new InputStreamReader(is, Charsets.UTF_8));
assertThat(data, is(content));
}
static DataAttachment randomDataAttachment() { static DataAttachment randomDataAttachment() {
return randomFrom(DataAttachment.JSON, DataAttachment.YAML, null); return randomFrom(DataAttachment.JSON, DataAttachment.YAML, null);
} }
private HttpRequest randomExternalAttachment() throws Exception {
if (randomBoolean()) {
Map<String, String[]> headers = new HashMap<>(1);
headers.put(HttpHeaders.Names.CONTENT_TYPE, new String[]{"plain/text"});
String content = "My wonderful text";
HttpResponse mockResponse = new HttpResponse(200, content, headers);
when(httpClient.execute(any(HttpRequest.class))).thenReturn(mockResponse);
return HttpRequest.builder("_host", 443).build();
} else {
return null;
}
}
} }

View File

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is;
public class DataAttachmentParserTests extends ESTestCase {
public void testSerializationWorks() throws Exception {
Map<String, EmailAttachmentParser> attachmentParsers = new HashMap<>();
attachmentParsers.put(DataAttachmentParser.TYPE, new DataAttachmentParser());
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
String id = "some-id";
XContentBuilder builder = jsonBuilder().startObject().startObject(id)
.startObject(DataAttachmentParser.TYPE).field("format", randomFrom("yaml", "json")).endObject()
.endObject().endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
logger.info("JSON: {}", builder.string());
EmailAttachments emailAttachments = emailAttachmentsParser.parse(parser);
assertThat(emailAttachments.getAttachments(), hasSize(1));
XContentBuilder toXcontentBuilder = jsonBuilder().startObject();
emailAttachments.getAttachments().get(0).toXContent(toXcontentBuilder, ToXContent.EMPTY_PARAMS);
toXcontentBuilder.endObject();
assertThat(toXcontentBuilder.string(), is(builder.string()));
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import com.google.common.base.Charsets;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import org.elasticsearch.watcher.support.http.Scheme;
import org.elasticsearch.watcher.watch.Payload;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.mock;
public class EmailAttachmentParsersTests extends ESTestCase {
private WatchExecutionContext ctx = mock(WatchExecutionContext.class);
public void testThatCustomParsersCanBeRegistered() throws Exception {
Map<String, EmailAttachmentParser> parsers = new HashMap<>();
parsers.put("test", new TestEmailAttachmentParser());
EmailAttachmentsParser parser = new EmailAttachmentsParser(parsers);
XContentBuilder builder = jsonBuilder();
builder.startObject()
.startObject("my-id")
.startObject("test")
.field("foo", "bar")
.endObject()
.endObject()
.startObject("my-other-id")
.startObject("test")
.field("foo", "baz")
.endObject()
.endObject()
.endObject();
logger.info("JSON: {}", builder.string());
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
EmailAttachments attachments = parser.parse(xContentParser);
assertThat(attachments.getAttachments(), hasSize(2));
EmailAttachmentParser.EmailAttachment emailAttachment = attachments.getAttachments().get(0);
assertThat(emailAttachment, instanceOf(TestEmailAttachment.class));
Attachment attachment = parsers.get("test").toAttachment(ctx, new Payload.Simple(), emailAttachment);
assertThat(attachment.name(), is("my-id"));
assertThat(attachment.contentType(), is("personalContentType"));
assertThat(parsers.get("test").toAttachment(ctx, new Payload.Simple(), attachments.getAttachments().get(1)).id(), is("my-other-id"));
}
public void testThatUnknownParserThrowsException() throws IOException {
EmailAttachmentsParser parser = new EmailAttachmentsParser(Collections.emptyMap());
XContentBuilder builder = jsonBuilder();
String type = randomAsciiOfLength(8);
builder.startObject().startObject("some-id").startObject(type);
XContentParser xContentParser = JsonXContent.jsonXContent.createParser(builder.bytes());
try {
parser.parse(xContentParser);
fail("Expected random parser of type [" + type + "] to throw an exception");
} catch (ElasticsearchParseException e) {
assertThat(e.getMessage(), containsString("Cannot parse attachment of type " + type));
}
}
public void testThatToXContentSerializationWorks() throws Exception {
List<EmailAttachmentParser.EmailAttachment> attachments = new ArrayList<>();
attachments.add(new DataAttachment("my-id", org.elasticsearch.watcher.actions.email.DataAttachment.JSON));
HttpRequestTemplate requestTemplate = HttpRequestTemplate.builder("localhost", 80).scheme(Scheme.HTTP).path("/").build();
HttpRequestAttachment httpRequestAttachment = new HttpRequestAttachment("other-id", requestTemplate, null);
attachments.add(httpRequestAttachment);
EmailAttachments emailAttachments = new EmailAttachments(attachments);
XContentBuilder builder = jsonBuilder();
emailAttachments.toXContent(builder, ToXContent.EMPTY_PARAMS);
logger.info("JSON is: " + builder.string());
assertThat(builder.string(), containsString("my-id"));
assertThat(builder.string(), containsString("json"));
assertThat(builder.string(), containsString("other-id"));
assertThat(builder.string(), containsString("localhost"));
assertThat(builder.string(), containsString("/"));
}
public class TestEmailAttachmentParser implements EmailAttachmentParser<TestEmailAttachment> {
@Override
public String type() {
return "test";
}
@Override
public TestEmailAttachment parse(String id, XContentParser parser) throws IOException {
TestEmailAttachment attachment = null;
String currentFieldName = null;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else {
if ("foo".equals(currentFieldName)) {
attachment = new TestEmailAttachment(id, parser.text());
}
}
}
if (attachment == null) {
throw new ElasticsearchParseException("Expected test parser to have field [foo]");
}
return attachment;
}
@Override
public Attachment toAttachment(WatchExecutionContext ctx, Payload payload, TestEmailAttachment attachment) {
return new Attachment.Bytes(attachment.getId(), attachment.getValue().getBytes(Charsets.UTF_8), "personalContentType");
}
}
public static class TestEmailAttachment implements EmailAttachmentParser.EmailAttachment {
private final String value;
private final String id;
interface Fields {
ParseField FOO = new ParseField("foo");
}
public TestEmailAttachment(String id, String value) {
this.id = id;
this.value = value;
}
@Override
public String type() {
return "test";
}
public String getValue() {
return value;
}
public String getId() {
return id;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.startObject(id)
.startObject(type())
.field(Fields.FOO.getPreferredName(), value)
.endObject()
.endObject();
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.watcher.actions.email.service.attachment;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.watcher.support.http.HttpClient;
import org.elasticsearch.watcher.support.http.HttpRequest;
import org.elasticsearch.watcher.support.http.HttpRequestTemplate;
import org.elasticsearch.watcher.support.http.HttpRequestTemplateTests;
import org.elasticsearch.watcher.support.http.HttpResponse;
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuth;
import org.elasticsearch.watcher.support.http.auth.basic.BasicAuthFactory;
import org.elasticsearch.watcher.support.secret.SecretService;
import org.junit.Before;
import java.util.HashMap;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonMap;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.core.Is.is;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class HttpEmailAttachementParserTests extends ESTestCase {
private SecretService.PlainText secretService;
private HttpAuthRegistry authRegistry;
private HttpRequestTemplate.Parser httpRequestTemplateParser;
private HttpClient httpClient;
@Before
public void init() throws Exception {
secretService = new SecretService.PlainText();
authRegistry = new HttpAuthRegistry(singletonMap(BasicAuth.TYPE, new BasicAuthFactory(secretService)));
httpRequestTemplateParser = new HttpRequestTemplate.Parser(authRegistry);
httpClient = mock(HttpClient.class);
HttpResponse response = new HttpResponse(200, "This is my response".getBytes(UTF_8));
when(httpClient.execute(any(HttpRequest.class))).thenReturn(response);
}
public void testSerializationWorks() throws Exception {
Map<String, EmailAttachmentParser> attachmentParsers = new HashMap<>();
attachmentParsers.put(HttpEmailAttachementParser.TYPE, new HttpEmailAttachementParser(httpClient, httpRequestTemplateParser, new HttpRequestTemplateTests.MockTextTemplateEngine()));
EmailAttachmentsParser emailAttachmentsParser = new EmailAttachmentsParser(attachmentParsers);
String id = "some-id";
XContentBuilder builder = jsonBuilder().startObject().startObject(id)
.startObject(HttpEmailAttachementParser.TYPE)
.startObject("request")
.field("scheme", "http")
.field("host", "test.de")
.field("port", 80)
.field("method", "get")
.field("path", "/foo")
.startObject("params").endObject()
.startObject("headers").endObject()
.endObject();
boolean configureContentType = randomBoolean();
if (configureContentType) {
builder.field("content_type", "application/foo");
}
builder.endObject().endObject().endObject();
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
logger.info("JSON: {}", builder.string());
EmailAttachments emailAttachments = emailAttachmentsParser.parse(parser);
assertThat(emailAttachments.getAttachments(), hasSize(1));
XContentBuilder toXcontentBuilder = jsonBuilder().startObject();
emailAttachments.getAttachments().get(0).toXContent(toXcontentBuilder, ToXContent.EMPTY_PARAMS);
toXcontentBuilder.endObject();
assertThat(toXcontentBuilder.string(), is(builder.string()));
}
}

View File

@ -169,7 +169,7 @@ public class HttpRequestTemplateTests extends ESTestCase {
assertThat(parsedTemplate, is(urlParsedTemplate)); assertThat(parsedTemplate, is(urlParsedTemplate));
} }
static class MockTextTemplateEngine implements TextTemplateEngine { public static class MockTextTemplateEngine implements TextTemplateEngine {
@Override @Override
public String render(TextTemplate template, Map<String, Object> model) { public String render(TextTemplate template, Map<String, Object> model) {
return template.getTemplate(); return template.getTemplate();

View File

@ -74,6 +74,7 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -210,8 +211,8 @@ public final class WatcherTestUtils {
Authentication auth = new Authentication("testname", new Secret("testpassword".toCharArray())); Authentication auth = new Authentication("testname", new Secret("testpassword".toCharArray()));
EmailAction action = new EmailAction(email, "testaccount", auth, Profile.STANDARD, null); EmailAction action = new EmailAction(email, "testaccount", auth, Profile.STANDARD, null, null);
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine, new HtmlSanitizer(Settings.EMPTY)); ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine, new HtmlSanitizer(Settings.EMPTY), Collections.emptyMap());
actions.add(new ActionWrapper("_email", executale)); actions.add(new ActionWrapper("_email", executale));

View File

@ -30,6 +30,7 @@ import org.elasticsearch.watcher.actions.email.service.EmailService;
import org.elasticsearch.watcher.actions.email.service.EmailTemplate; import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer; import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
import org.elasticsearch.watcher.actions.email.service.Profile; import org.elasticsearch.watcher.actions.email.service.Profile;
import org.elasticsearch.watcher.actions.email.service.attachment.EmailAttachmentsParser;
import org.elasticsearch.watcher.actions.index.ExecutableIndexAction; import org.elasticsearch.watcher.actions.index.ExecutableIndexAction;
import org.elasticsearch.watcher.actions.index.IndexAction; import org.elasticsearch.watcher.actions.index.IndexAction;
import org.elasticsearch.watcher.actions.index.IndexActionFactory; import org.elasticsearch.watcher.actions.index.IndexActionFactory;
@ -420,8 +421,8 @@ public class WatchTests extends ESTestCase {
List<ActionWrapper> list = new ArrayList<>(); List<ActionWrapper> list = new ArrayList<>();
if (randomBoolean()) { if (randomBoolean()) {
ExecutableTransform transform = randomTransform(); ExecutableTransform transform = randomTransform();
EmailAction action = new EmailAction(EmailTemplate.builder().build(), null, null, Profile.STANDARD, randomFrom(DataAttachment.JSON, DataAttachment.YAML, null)); EmailAction action = new EmailAction(EmailTemplate.builder().build(), null, null, Profile.STANDARD, randomFrom(DataAttachment.JSON, DataAttachment.YAML, null), null);
list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine, htmlSanitizer))); list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine, htmlSanitizer, Collections.emptyMap())));
} }
if (randomBoolean()) { if (randomBoolean()) {
DateTimeZone timeZone = randomBoolean() ? DateTimeZone.UTC : null; DateTimeZone timeZone = randomBoolean() ? DateTimeZone.UTC : null;
@ -445,7 +446,7 @@ public class WatchTests extends ESTestCase {
for (ActionWrapper action : actions) { for (ActionWrapper action : actions) {
switch (action.action().type()) { switch (action.action().type()) {
case EmailAction.TYPE: case EmailAction.TYPE:
parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine, htmlSanitizer)); parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine, htmlSanitizer, new EmailAttachmentsParser(Collections.emptyMap()), Collections.emptyMap()));
break; break;
case IndexAction.TYPE: case IndexAction.TYPE:
parsers.put(IndexAction.TYPE, new IndexActionFactory(settings, client)); parsers.put(IndexAction.TYPE, new IndexActionFactory(settings, client));