Merge branch 'master' into upgrade_to_es_20
Conflicts: src/main/java/org/elasticsearch/watcher/support/http/HttpClient.java src/main/java/org/elasticsearch/watcher/support/http/HttpRequestTemplate.java src/main/java/org/elasticsearch/watcher/transport/actions/execute/ExecuteWatchRequestBuilder.java src/main/java/org/elasticsearch/watcher/transport/actions/execute/TransportExecuteWatchAction.java src/test/java/org/elasticsearch/watcher/actions/email/EmailActionTests.java src/test/java/org/elasticsearch/watcher/actions/email/service/HtmlSanitizeTests.java src/test/java/org/elasticsearch/watcher/actions/webhook/WebhookHttpsIntegrationTests.java src/test/java/org/elasticsearch/watcher/execution/ManualExecutionTests.java src/test/java/org/elasticsearch/watcher/support/http/HttpClientTests.java src/test/java/org/elasticsearch/watcher/support/http/HttpRequestTemplateTests.java Original commit: elastic/x-pack-elasticsearch@61885ed389
This commit is contained in:
commit
10c5ac6353
|
@ -4,12 +4,11 @@
|
|||
"methods": [ "PUT", "POST" ],
|
||||
"url": {
|
||||
"path": "/_watcher/watch/{id}/_execute",
|
||||
"paths": [ "/_watcher/watch/{id}/_execute" ],
|
||||
"paths": [ "/_watcher/watch/{id}/_execute", "/_watcher/watch/_execute" ],
|
||||
"parts": {
|
||||
"id": {
|
||||
"type" : "string",
|
||||
"description" : "Watch ID",
|
||||
"required" : true
|
||||
"description" : "Watch ID"
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
|
|
|
@ -77,8 +77,12 @@
|
|||
- match: { "watch_record.trigger_event.type": "manual" }
|
||||
- match: { "watch_record.trigger_event.triggered_time": "2015-05-05T20:58:02.443Z" }
|
||||
- match: { "watch_record.trigger_event.manual.schedule.scheduled_time": "2015-05-05T20:58:02.443Z" }
|
||||
- match: { "watch_record.result.condition.met": true }
|
||||
- match: { "watch_record.result.input.type": "simple" }
|
||||
- match: { "watch_record.result.input.status": "success" }
|
||||
- match: { "watch_record.result.input.payload.foo": "bar" }
|
||||
- match: { "watch_record.result.condition.type": "always" }
|
||||
- match: { "watch_record.result.condition.status": "success" }
|
||||
- match: { "watch_record.result.condition.met": true }
|
||||
- match: { "watch_record.result.actions.0.id" : "email_admin" }
|
||||
- match: { "watch_record.result.actions.0.status" : "simulated" }
|
||||
- match: { "watch_record.result.actions.0.type" : "email" }
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
|
||||
- match: { "watch_record.watch_id": "my_logging_watch" }
|
||||
- match: { "watch_record.state": "executed" }
|
||||
- match: { "watch_record.result.input.type": "simple" }
|
||||
- match: { "watch_record.result.input.status": "success" }
|
||||
- match: { "watch_record.result.input.payload.count": 1 }
|
||||
- match: { "watch_record.result.condition.type": "script" }
|
||||
- match: { "watch_record.result.condition.status": "success" }
|
||||
- match: { "watch_record.result.condition.met": true }
|
||||
- match: { "watch_record.result.actions.0.id" : "logging" }
|
||||
- match: { "watch_record.result.actions.0.type" : "logging" }
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
"Test execute watch api with an inline watch":
|
||||
- do:
|
||||
cluster.health:
|
||||
wait_for_status: green
|
||||
|
||||
- do:
|
||||
watcher.execute_watch:
|
||||
body: >
|
||||
{
|
||||
"trigger_data" : {
|
||||
"scheduled_time" : "2015-05-05T20:58:02.443Z",
|
||||
"triggered_time" : "2015-05-05T20:58:02.443Z"
|
||||
},
|
||||
"alternative_input" : {
|
||||
"foo" : "bar"
|
||||
},
|
||||
"ignore_condition" : true,
|
||||
"action_modes" : {
|
||||
"_all" : "force_simulate"
|
||||
},
|
||||
"watch" : {
|
||||
"trigger" : {
|
||||
"schedule" : { "cron" : "0 0 0 1 * ? 2099" }
|
||||
},
|
||||
"input" : {
|
||||
"search" : {
|
||||
"request" : {
|
||||
"indices" : [ "logstash*" ],
|
||||
"body" : {
|
||||
"query" : {
|
||||
"filtered": {
|
||||
"query": {
|
||||
"match": {
|
||||
"response": 404
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
"range": {
|
||||
"@timestamp" : {
|
||||
"from": "{{ctx.trigger.scheduled_time}}||-5m",
|
||||
"to": "{{ctx.trigger.triggered_time}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"condition" : {
|
||||
"script" : {
|
||||
"inline" : "ctx.payload.hits.total > 1"
|
||||
}
|
||||
},
|
||||
"actions" : {
|
||||
"email_admin" : {
|
||||
"email" : {
|
||||
"to" : "someone@domain.host.com",
|
||||
"subject" : "404 recently encountered"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
- match: { "watch_record.state": "executed" }
|
||||
- match: { "watch_record.trigger_event.manual.schedule.scheduled_time": "2015-05-05T20:58:02.443Z" }
|
||||
- match: { "watch_record.result.input.type": "simple" }
|
||||
- match: { "watch_record.result.input.payload.foo": "bar" }
|
||||
- match: { "watch_record.result.condition.met": true }
|
||||
- match: { "watch_record.result.actions.0.id" : "email_admin" }
|
||||
- match: { "watch_record.result.actions.0.status" : "simulated" }
|
||||
- match: { "watch_record.result.actions.0.email.email.subject" : "404 recently encountered" }
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.inject.multibindings.MapBinder;
|
|||
import org.elasticsearch.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.EmailActionFactory;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.actions.email.service.InternalEmailService;
|
||||
import org.elasticsearch.watcher.actions.index.IndexAction;
|
||||
import org.elasticsearch.watcher.actions.index.IndexActionFactory;
|
||||
|
@ -54,6 +55,7 @@ public class ActionModule extends AbstractModule {
|
|||
}
|
||||
|
||||
bind(ActionRegistry.class).asEagerSingleton();
|
||||
bind(HtmlSanitizer.class).asEagerSingleton();
|
||||
bind(EmailService.class).to(InternalEmailService.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
|
|
|
@ -78,9 +78,13 @@ public class ActionWrapper implements ToXContent {
|
|||
if (transform != null) {
|
||||
try {
|
||||
transformResult = transform.execute(ctx, payload);
|
||||
if (transformResult.status() == Transform.Result.Status.FAILURE) {
|
||||
action.logger().error("failed to execute action [{}/{}]. failed to transform payload. {}", ctx.watch().id(), id, transformResult.reason());
|
||||
return new ActionWrapper.Result(id, transformResult, new Action.Result.Failure(action.type(), "Failed to transform payload"));
|
||||
}
|
||||
payload = transformResult.payload();
|
||||
} catch (Exception e) {
|
||||
action.logger.error("failed to execute action [{}/{}]. failed to transform payload.", e, ctx.watch().id(), id);
|
||||
action.logger().error("failed to execute action [{}/{}]. failed to transform payload.", e, ctx.watch().id(), id);
|
||||
return new ActionWrapper.Result(id, new Action.Result.Failure(action.type(), "Failed to transform payload. error: " + ExceptionsHelper.detailedMessage(e)));
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +92,7 @@ public class ActionWrapper implements ToXContent {
|
|||
Action.Result actionResult = action.execute(id, ctx, payload);
|
||||
return new ActionWrapper.Result(id, transformResult, actionResult);
|
||||
} catch (Exception e) {
|
||||
action.logger.error("failed to execute action [{}/{}]", e, ctx.watch().id(), id);
|
||||
action.logger().error("failed to execute action [{}/{}]", e, ctx.watch().id(), id);
|
||||
return new ActionWrapper.Result(id, new Action.Result.Failure(action.type(), ExceptionsHelper.detailedMessage(e)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,13 @@ public abstract class ExecutableAction<A extends Action> implements ToXContent {
|
|||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* yack... needed to expose that for testing purposes
|
||||
*/
|
||||
public ESLogger logger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
public abstract Action.Result execute(String actionId, WatchExecutionContext context, Payload payload) throws Exception;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -113,8 +113,8 @@ public class EmailAction implements Action {
|
|||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static EmailAction parse(String watchId, String actionId, XContentParser parser, boolean sanitizeHtmlBody) throws IOException {
|
||||
EmailTemplate.Parser emailParser = new EmailTemplate.Parser(sanitizeHtmlBody);
|
||||
public static EmailAction parse(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
EmailTemplate.Parser emailParser = new EmailTemplate.Parser();
|
||||
String account = null;
|
||||
String user = null;
|
||||
Secret password = null;
|
||||
|
|
|
@ -9,10 +9,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -24,16 +23,14 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
|
|||
|
||||
private final EmailService emailService;
|
||||
private final TemplateEngine templateEngine;
|
||||
private final boolean sanitizeHtmlBodyOfEmails;
|
||||
|
||||
private static String SANITIZE_HTML_SETTING = "watcher.actions.email.sanitize_html";
|
||||
private final HtmlSanitizer htmlSanitizer;
|
||||
|
||||
@Inject
|
||||
public EmailActionFactory(Settings settings, EmailService emailService, TemplateEngine templateEngine) {
|
||||
public EmailActionFactory(Settings settings, EmailService emailService, TemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) {
|
||||
super(Loggers.getLogger(ExecutableEmailAction.class, settings));
|
||||
this.emailService = emailService;
|
||||
this.templateEngine = templateEngine;
|
||||
sanitizeHtmlBodyOfEmails = settings.getAsBoolean(SANITIZE_HTML_SETTING, true);
|
||||
this.htmlSanitizer = htmlSanitizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,11 +40,11 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
|
|||
|
||||
@Override
|
||||
public EmailAction parseAction(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
return EmailAction.parse(watchId, actionId, parser, sanitizeHtmlBodyOfEmails);
|
||||
return EmailAction.parse(watchId, actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableEmailAction createExecutable(EmailAction action) {
|
||||
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine);
|
||||
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine, htmlSanitizer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.watcher.actions.ExecutableAction;
|
|||
import org.elasticsearch.watcher.actions.email.service.Attachment;
|
||||
import org.elasticsearch.watcher.actions.email.service.Email;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.support.Variables;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
|
@ -25,11 +26,13 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
|
|||
|
||||
final EmailService emailService;
|
||||
final TemplateEngine templateEngine;
|
||||
final HtmlSanitizer htmlSanitizer;
|
||||
|
||||
public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TemplateEngine templateEngine) {
|
||||
public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) {
|
||||
super(action, logger);
|
||||
this.emailService = emailService;
|
||||
this.templateEngine = templateEngine;
|
||||
this.htmlSanitizer = htmlSanitizer;
|
||||
}
|
||||
|
||||
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
|
||||
|
@ -42,7 +45,7 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
|
|||
attachments.put(attachment.id(), attachment);
|
||||
}
|
||||
|
||||
Email.Builder email = action.getEmail().render(templateEngine, model, attachments);
|
||||
Email.Builder email = action.getEmail().render(templateEngine, model, htmlSanitizer, attachments);
|
||||
email.id(ctx.id().value());
|
||||
|
||||
if (ctx.simulateAction(actionId)) {
|
||||
|
|
|
@ -11,9 +11,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
import org.owasp.html.*;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.mail.internet.AddressException;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
@ -32,11 +30,10 @@ public class EmailTemplate implements ToXContent {
|
|||
final Template subject;
|
||||
final Template textBody;
|
||||
final Template htmlBody;
|
||||
final boolean sanitizeHtmlBody;
|
||||
|
||||
public EmailTemplate(Template from, Template[] replyTo, Template priority, Template[] to,
|
||||
Template[] cc, Template[] bcc, Template subject, Template textBody,
|
||||
Template htmlBody, boolean sanitizeHtmlBody) {
|
||||
Template htmlBody) {
|
||||
this.from = from;
|
||||
this.replyTo = replyTo;
|
||||
this.priority = priority;
|
||||
|
@ -46,7 +43,6 @@ public class EmailTemplate implements ToXContent {
|
|||
this.subject = subject;
|
||||
this.textBody = textBody;
|
||||
this.htmlBody = htmlBody;
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public Template from() {
|
||||
|
@ -85,11 +81,7 @@ public class EmailTemplate implements ToXContent {
|
|||
return htmlBody;
|
||||
}
|
||||
|
||||
public boolean sanitizeHtmlBody() {
|
||||
return sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public Email.Builder render(TemplateEngine engine, Map<String, Object> model, Map<String, Attachment> attachments) throws AddressException {
|
||||
public Email.Builder render(TemplateEngine engine, Map<String, Object> model, HtmlSanitizer htmlSanitizer, Map<String, Attachment> attachments) throws AddressException {
|
||||
Email.Builder builder = Email.builder();
|
||||
if (from != null) {
|
||||
builder.from(engine.render(from, model));
|
||||
|
@ -126,9 +118,7 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
if (htmlBody != null) {
|
||||
String renderedHtml = engine.render(htmlBody, model);
|
||||
if (sanitizeHtmlBody && htmlBody != null) {
|
||||
renderedHtml = sanitizeHtml(renderedHtml, attachments);
|
||||
}
|
||||
renderedHtml = htmlSanitizer.sanitize(renderedHtml);
|
||||
builder.htmlBody(renderedHtml);
|
||||
}
|
||||
return builder;
|
||||
|
@ -236,7 +226,6 @@ public class EmailTemplate implements ToXContent {
|
|||
private Template subject;
|
||||
private Template textBody;
|
||||
private Template htmlBody;
|
||||
private boolean sanitizeHtmlBody = true;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
@ -377,81 +366,27 @@ public class EmailTemplate implements ToXContent {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder htmlBody(String html, boolean sanitizeHtmlBody) {
|
||||
return htmlBody(Template.defaultType(html), sanitizeHtmlBody);
|
||||
public Builder htmlBody(String html) {
|
||||
return htmlBody(Template.defaultType(html));
|
||||
}
|
||||
|
||||
public Builder htmlBody(Template.Builder html, boolean sanitizeHtmlBody) {
|
||||
return htmlBody(html.build(), sanitizeHtmlBody);
|
||||
public Builder htmlBody(Template.Builder html) {
|
||||
return htmlBody(html.build());
|
||||
}
|
||||
|
||||
public Builder htmlBody(Template html, boolean sanitizeHtmlBody) {
|
||||
public Builder htmlBody(Template html) {
|
||||
this.htmlBody = html;
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EmailTemplate build() {
|
||||
return new EmailTemplate(from, replyTo, priority, to, cc, bcc, subject, textBody, htmlBody, sanitizeHtmlBody);
|
||||
}
|
||||
}
|
||||
|
||||
static String sanitizeHtml(String html, final Map<String, Attachment> attachments){
|
||||
ElementPolicy onlyCIDImgPolicy = new AttachementVerifyElementPolicy(attachments);
|
||||
PolicyFactory policy = Sanitizers.FORMATTING
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowElements("img", "table", "tr", "td", "style", "body", "head", "hr")
|
||||
.allowAttributes("src").onElements("img")
|
||||
.allowAttributes("class").onElements("style")
|
||||
.allowUrlProtocols("cid")
|
||||
.allowCommonInlineFormattingElements()
|
||||
.allowElements(onlyCIDImgPolicy, "img")
|
||||
.allowStyling(CssSchema.DEFAULT)
|
||||
.toFactory())
|
||||
.and(Sanitizers.LINKS)
|
||||
.and(Sanitizers.BLOCKS);
|
||||
return policy.sanitize(html);
|
||||
}
|
||||
|
||||
private static class AttachementVerifyElementPolicy implements ElementPolicy {
|
||||
|
||||
private final Map<String, Attachment> attachments;
|
||||
|
||||
AttachementVerifyElementPolicy(Map<String, Attachment> attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(@ParametersAreNonnullByDefault String elementName, @ParametersAreNonnullByDefault List<String> attrs) {
|
||||
if (attrs.size() == 0) {
|
||||
return elementName;
|
||||
}
|
||||
for (int i = 0; i < attrs.size(); ++i) {
|
||||
if(attrs.get(i).equals("src") && i < attrs.size() - 1) {
|
||||
String srcValue = attrs.get(i+1);
|
||||
if (!srcValue.startsWith("cid:")) {
|
||||
return null; //Disallow anything other than content ids
|
||||
}
|
||||
String contentId = srcValue.substring(4);
|
||||
if (attachments.containsKey(contentId)) {
|
||||
return elementName;
|
||||
} else {
|
||||
return null; //This cid wasn't found
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementName;
|
||||
return new EmailTemplate(from, replyTo, priority, to, cc, bcc, subject, textBody, htmlBody);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
|
||||
private final EmailTemplate.Builder builder = builder();
|
||||
private final boolean sanitizeHtmlBody;
|
||||
|
||||
public Parser(boolean sanitizeHtmlBody) {
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public boolean handle(String fieldName, XContentParser parser) throws IOException {
|
||||
if (Email.Field.FROM.match(fieldName)) {
|
||||
|
@ -514,7 +449,7 @@ public class EmailTemplate implements ToXContent {
|
|||
} else if (Email.Field.BODY_TEXT.match(currentFieldName)) {
|
||||
builder.textBody(Template.parse(parser));
|
||||
} else if (Email.Field.BODY_HTML.match(currentFieldName)) {
|
||||
builder.htmlBody(Template.parse(parser), sanitizeHtmlBody);
|
||||
builder.htmlBody(Template.parse(parser));
|
||||
} else {
|
||||
throw new ParseException("could not parse email template. unknown field [{}.{}] field", fieldName, currentFieldName);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.owasp.html.CssSchema;
|
||||
import org.owasp.html.ElementPolicy;
|
||||
import org.owasp.html.HtmlPolicyBuilder;
|
||||
import org.owasp.html.PolicyFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HtmlSanitizer {
|
||||
|
||||
static final String[] FORMATTING_TAGS = new String[] {
|
||||
"b", "i", "s", "u", "o", "sup", "sub", "ins", "del", "strong",
|
||||
"strike", "tt", "code", "big", "small", "br", "span", "em"
|
||||
};
|
||||
static final String[] BLOCK_TAGS = new String[] {
|
||||
"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "blockquote"
|
||||
};
|
||||
static final String[] TABLE_TAGS = new String[] {
|
||||
"table", "hr", "tr", "td"
|
||||
};
|
||||
static final String[] DEFAULT_ALLOWED = new String[] {
|
||||
"body", "head", "_tables", "_links", "_blocks", "_formatting", "img:embedded"
|
||||
};
|
||||
|
||||
private final boolean enabled;
|
||||
private final PolicyFactory policy;
|
||||
|
||||
@Inject
|
||||
public HtmlSanitizer(Settings settings) {
|
||||
enabled = settings.getAsBoolean("watcher.actions.email.html.sanitization.enabled", true);
|
||||
String[] allow = settings.getAsArray("watcher.actions.email.html.sanitization.allow", DEFAULT_ALLOWED);
|
||||
String[] disallow = settings.getAsArray("watcher.actions.email.html.sanitization.disallow", Strings.EMPTY_ARRAY);
|
||||
policy = createCommonPolicy(allow, disallow);
|
||||
}
|
||||
|
||||
public String sanitize(String html) {
|
||||
if (!enabled) {
|
||||
return html;
|
||||
}
|
||||
return policy.sanitize(html);
|
||||
}
|
||||
|
||||
static PolicyFactory createCommonPolicy(String[] allow, String[] disallow) {
|
||||
HtmlPolicyBuilder policyBuilder = new HtmlPolicyBuilder();
|
||||
|
||||
if (Arrays.binarySearch(allow, "_all") > -1) {
|
||||
return policyBuilder
|
||||
.allowElements(TABLE_TAGS)
|
||||
.allowElements(BLOCK_TAGS)
|
||||
.allowElements(FORMATTING_TAGS)
|
||||
.allowStyling(CssSchema.DEFAULT)
|
||||
.allowStandardUrlProtocols().allowElements("a")
|
||||
.allowAttributes("href").onElements("a").requireRelNofollowOnLinks()
|
||||
.allowElements("img")
|
||||
.allowAttributes("src").onElements("img")
|
||||
.allowStandardUrlProtocols()
|
||||
.allowUrlProtocols("cid")
|
||||
.toFactory();
|
||||
}
|
||||
|
||||
EnumSet<Images> images = EnumSet.noneOf(Images.class);
|
||||
|
||||
for (String tag : allow) {
|
||||
tag = tag.toLowerCase(Locale.ROOT);
|
||||
switch (tag) {
|
||||
case "_tables":
|
||||
policyBuilder.allowElements(TABLE_TAGS);
|
||||
break;
|
||||
case "_links":
|
||||
policyBuilder.allowElements("a")
|
||||
.allowAttributes("href").onElements("a")
|
||||
.allowStandardUrlProtocols()
|
||||
.requireRelNofollowOnLinks();
|
||||
break;
|
||||
case "_blocks":
|
||||
policyBuilder.allowElements(BLOCK_TAGS);
|
||||
break;
|
||||
case "_formatting":
|
||||
policyBuilder.allowElements(FORMATTING_TAGS);
|
||||
break;
|
||||
case "_styles":
|
||||
policyBuilder.allowStyling(CssSchema.DEFAULT);
|
||||
break;
|
||||
case "img:all":
|
||||
case "img":
|
||||
images.add(Images.ALL);
|
||||
break;
|
||||
case "img:embedded":
|
||||
images.add(Images.EMBEDDED);
|
||||
break;
|
||||
default:
|
||||
policyBuilder.allowElements(tag);
|
||||
}
|
||||
}
|
||||
for (String tag : disallow) {
|
||||
tag = tag.toLowerCase(Locale.ROOT);
|
||||
switch (tag) {
|
||||
case "_tables":
|
||||
policyBuilder.disallowElements(TABLE_TAGS);
|
||||
break;
|
||||
case "_links":
|
||||
policyBuilder.disallowElements("a");
|
||||
break;
|
||||
case "_blocks":
|
||||
policyBuilder.disallowElements(BLOCK_TAGS);
|
||||
break;
|
||||
case "_formatting":
|
||||
policyBuilder.disallowElements(FORMATTING_TAGS);
|
||||
break;
|
||||
case "_styles":
|
||||
policyBuilder.disallowAttributes("style");
|
||||
break;
|
||||
case "img:all":
|
||||
case "img":
|
||||
images.remove(Images.ALL);
|
||||
break;
|
||||
case "img:embedded":
|
||||
images.remove(Images.EMBEDDED);
|
||||
break;
|
||||
default:
|
||||
policyBuilder.disallowElements(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!images.isEmpty()) {
|
||||
policyBuilder.allowAttributes("src").onElements("img").allowUrlProtocols("cid");
|
||||
if (images.contains(Images.ALL)) {
|
||||
policyBuilder.allowElements("img");
|
||||
policyBuilder.allowStandardUrlProtocols();
|
||||
} else {
|
||||
// embedded
|
||||
policyBuilder.allowElements(EmbeddedImgOnlyPolicy.INSTANCE, "img");
|
||||
}
|
||||
}
|
||||
|
||||
return policyBuilder.toFactory();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An {@code img} tag policy that only accept {@code cid:} values in its {@code src} attribute.
|
||||
* If such value is found, the content id is verified against the available attachements of the
|
||||
* email and if the content/attachment is not found, the element is dropped.
|
||||
*/
|
||||
private static class EmbeddedImgOnlyPolicy implements ElementPolicy {
|
||||
|
||||
private static EmbeddedImgOnlyPolicy INSTANCE = new EmbeddedImgOnlyPolicy();
|
||||
|
||||
@Override
|
||||
public String apply(String elementName, List<String> attrs) {
|
||||
if (!"img".equals(elementName) || attrs.size() == 0) {
|
||||
return elementName;
|
||||
}
|
||||
String attrName = null;
|
||||
for (String attr : attrs) {
|
||||
if (attrName == null) {
|
||||
attrName = attr.toLowerCase(Locale.ROOT);
|
||||
continue;
|
||||
}
|
||||
// reject external image source (only allow embedded ones)
|
||||
if ("src".equals(attrName) && !attr.startsWith("cid:")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return elementName;
|
||||
}
|
||||
}
|
||||
|
||||
enum Images {
|
||||
ALL,
|
||||
EMBEDDED
|
||||
}
|
||||
}
|
|
@ -5,11 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.condition;
|
||||
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -20,25 +22,64 @@ public interface Condition extends ToXContent {
|
|||
|
||||
abstract class Result implements ToXContent {
|
||||
|
||||
public enum Status {
|
||||
SUCCESS, FAILURE
|
||||
}
|
||||
|
||||
protected final String type;
|
||||
protected final Status status;
|
||||
private final String reason;
|
||||
protected final boolean met;
|
||||
|
||||
public Result(String type, boolean met) {
|
||||
this.status = Status.SUCCESS;
|
||||
this.type = type;
|
||||
this.met = met;
|
||||
this.reason = null;
|
||||
}
|
||||
|
||||
protected Result(String type, Exception e) {
|
||||
this.status = Status.FAILURE;
|
||||
this.type = type;
|
||||
this.met = false;
|
||||
this.reason = ExceptionsHelper.detailedMessage(e);
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean met() { return met; }
|
||||
public Status status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean met() {
|
||||
assert status == Status.SUCCESS;
|
||||
return met;
|
||||
}
|
||||
|
||||
public String reason() {
|
||||
assert status == Status.FAILURE;
|
||||
return reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.TYPE.getPreferredName(), type);
|
||||
builder.field(Field.MET.getPreferredName(), met);
|
||||
builder.field(Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT));
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assert reason == null;
|
||||
builder.field(Field.MET.getPreferredName(), met);
|
||||
break;
|
||||
case FAILURE:
|
||||
assert reason != null && !met;
|
||||
builder.field(Field.REASON.getPreferredName(), reason);
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
typeXContent(builder, params);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
@ -53,6 +94,8 @@ public interface Condition extends ToXContent {
|
|||
|
||||
interface Field {
|
||||
ParseField TYPE = new ParseField("type");
|
||||
ParseField STATUS = new ParseField("status");
|
||||
ParseField MET = new ParseField("met");
|
||||
ParseField REASON = new ParseField("reason");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ public abstract class ExecutableCondition<C extends Condition, R extends Conditi
|
|||
/**
|
||||
* Executes this condition
|
||||
*/
|
||||
public abstract R execute(WatchExecutionContext ctx) throws IOException;
|
||||
public abstract R execute(WatchExecutionContext ctx);
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
|
|
|
@ -21,7 +21,7 @@ public class ExecutableAlwaysCondition extends ExecutableCondition<AlwaysConditi
|
|||
}
|
||||
|
||||
@Override
|
||||
public AlwaysCondition.Result execute(WatchExecutionContext ctx) throws IOException {
|
||||
public AlwaysCondition.Result execute(WatchExecutionContext ctx) {
|
||||
return AlwaysCondition.Result.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.condition.compare;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -125,19 +126,27 @@ public class CompareCondition implements Condition {
|
|||
|
||||
public static class Result extends Condition.Result {
|
||||
|
||||
private final Map<String, Object> resolveValues;
|
||||
private final @Nullable Map<String, Object> resolveValues;
|
||||
|
||||
Result(Map<String, Object> resolveValues, boolean met) {
|
||||
super(TYPE, met);
|
||||
this.resolveValues = resolveValues;
|
||||
}
|
||||
|
||||
Result(@Nullable Map<String, Object> resolveValues, Exception e) {
|
||||
super(TYPE, e);
|
||||
this.resolveValues = resolveValues;
|
||||
}
|
||||
|
||||
public Map<String, Object> getResolveValues() {
|
||||
return resolveValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (resolveValues == null) {
|
||||
return builder;
|
||||
}
|
||||
return builder.startObject(type)
|
||||
.field(Field.RESOLVED_VALUES.getPreferredName(), resolveValues)
|
||||
.endObject();
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.watcher.condition.compare;
|
|||
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.watcher.actions.email.DataAttachment;
|
||||
import org.elasticsearch.watcher.condition.ExecutableCondition;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.support.Variables;
|
||||
|
@ -39,10 +40,21 @@ public class ExecutableCompareCondition extends ExecutableCondition<CompareCondi
|
|||
}
|
||||
|
||||
@Override
|
||||
public CompareCondition.Result execute(WatchExecutionContext ctx) throws IOException {
|
||||
Map<String, Object> model = Variables.createCtxModel(ctx, ctx.payload());
|
||||
|
||||
public CompareCondition.Result execute(WatchExecutionContext ctx) {
|
||||
Map<String, Object> resolvedValues = new HashMap<>();
|
||||
try {
|
||||
return doExecute(ctx, resolvedValues);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to execute [{}] condition for [{}]", CompareCondition.TYPE, ctx.id());
|
||||
if (resolvedValues.isEmpty()) {
|
||||
resolvedValues = null;
|
||||
}
|
||||
return new CompareCondition.Result(resolvedValues, e);
|
||||
}
|
||||
}
|
||||
|
||||
public CompareCondition.Result doExecute(WatchExecutionContext ctx, Map<String, Object> resolvedValues) throws Exception {
|
||||
Map<String, Object> model = Variables.createCtxModel(ctx, ctx.payload());
|
||||
|
||||
Object configuredValue = condition.getValue();
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public class ExecutableNeverCondition extends ExecutableCondition<NeverCondition
|
|||
}
|
||||
|
||||
@Override
|
||||
public NeverCondition.Result execute(WatchExecutionContext ctx) throws IOException {
|
||||
public NeverCondition.Result execute(WatchExecutionContext ctx) {
|
||||
return NeverCondition.Result.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
|||
import org.elasticsearch.watcher.support.Variables;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -35,20 +34,25 @@ public class ExecutableScriptCondition extends ExecutableCondition<ScriptConditi
|
|||
}
|
||||
|
||||
@Override
|
||||
public ScriptCondition.Result execute(WatchExecutionContext ctx) throws IOException {
|
||||
public ScriptCondition.Result execute(WatchExecutionContext ctx) {
|
||||
try {
|
||||
Map<String, Object> parameters = Variables.createCtxModel(ctx, ctx.payload());
|
||||
if (condition.script.params() != null && !condition.script.params().isEmpty()) {
|
||||
parameters.putAll(condition.script.params());
|
||||
}
|
||||
ExecutableScript executable = scriptService.executable(compiledScript, parameters);
|
||||
Object value = executable.run();
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value ? ScriptCondition.Result.MET : ScriptCondition.Result.UNMET;
|
||||
}
|
||||
throw new ScriptConditionException("failed to execute [{}] condition for watch [{}]. script [{}] must return a boolean value (true|false) but instead returned [{}]", type(), ctx.watch().id(), condition.script.script(), value);
|
||||
return doExecute(ctx);
|
||||
} catch (Exception e) {
|
||||
throw new ScriptConditionException("failed to execute [{}] condition for watch [{}]. script [{}] threw an exception", e, type(), ctx.watch().id(), condition.script.script());
|
||||
logger.error("failed to execute [{}] condition for [{}]", ScriptCondition.TYPE, ctx.id());
|
||||
return new ScriptCondition.Result(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptCondition.Result doExecute(WatchExecutionContext ctx) throws Exception {
|
||||
Map<String, Object> parameters = Variables.createCtxModel(ctx, ctx.payload());
|
||||
if (condition.script.params() != null && !condition.script.params().isEmpty()) {
|
||||
parameters.putAll(condition.script.params());
|
||||
}
|
||||
ExecutableScript executable = scriptService.executable(compiledScript, parameters);
|
||||
Object value = executable.run();
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value ? ScriptCondition.Result.MET : ScriptCondition.Result.UNMET;
|
||||
}
|
||||
throw new ScriptConditionException("script [{}] must return a boolean value (true|false) but instead returned [{}]", type(), ctx.watch().id(), condition.script.script(), value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,10 @@ public class ScriptCondition implements Condition {
|
|||
super(TYPE, met);
|
||||
}
|
||||
|
||||
Result(Exception e) {
|
||||
super(TYPE, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
|
|
|
@ -328,6 +328,9 @@ public class ExecutionService extends AbstractComponent {
|
|||
conditionResult = watch.condition().execute(ctx);
|
||||
ctx.onConditionResult(conditionResult);
|
||||
}
|
||||
if (conditionResult.status() == Condition.Result.Status.FAILURE) {
|
||||
return ctx.abortFailedExecution("failed to execute watch condition");
|
||||
}
|
||||
|
||||
if (conditionResult.met()) {
|
||||
|
||||
|
|
|
@ -121,6 +121,9 @@ public abstract class WatchExecutionContext {
|
|||
}
|
||||
beforeWatchTransform();
|
||||
this.transformResult = watch.transform().execute(this, payload);
|
||||
if (this.transformResult.status() == Transform.Result.Status.FAILURE) {
|
||||
throw new WatchExecutionException("failed to execute watch level transform for [{}]", id);
|
||||
}
|
||||
this.payload = transformResult.payload();
|
||||
this.transformedPayload = this.payload;
|
||||
return transformedPayload;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.execution;
|
||||
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class WatchExecutionException extends WatcherException {
|
||||
|
||||
public WatchExecutionException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public WatchExecutionException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
|
@ -27,10 +27,10 @@ public interface Input extends ToXContent {
|
|||
SUCCESS, FAILURE
|
||||
}
|
||||
|
||||
protected final Status status;
|
||||
protected final String type;
|
||||
private final Payload payload;
|
||||
protected final Status status;
|
||||
private final String reason;
|
||||
private final Payload payload;
|
||||
|
||||
protected Result(String type, Payload payload) {
|
||||
this.status = Status.SUCCESS;
|
||||
|
@ -67,8 +67,8 @@ public interface Input extends ToXContent {
|
|||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT));
|
||||
builder.field(Field.TYPE.getPreferredName(), type);
|
||||
builder.field(Field.STATUS.getPreferredName(), status.name().toLowerCase(Locale.ROOT));
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assert payload != null;
|
||||
|
|
|
@ -37,6 +37,8 @@ public class RestExecuteWatchAction extends WatcherRestHandler {
|
|||
super(settings, controller, client);
|
||||
controller.registerHandler(RestRequest.Method.POST, URI_BASE + "/watch/{id}/_execute", this);
|
||||
controller.registerHandler(RestRequest.Method.PUT, URI_BASE + "/watch/{id}/_execute", this);
|
||||
controller.registerHandler(RestRequest.Method.POST, URI_BASE + "/watch/_execute", this);
|
||||
controller.registerHandler(RestRequest.Method.PUT, URI_BASE + "/watch/_execute", this);
|
||||
this.triggerService = triggerService;
|
||||
}
|
||||
|
||||
|
@ -58,9 +60,8 @@ public class RestExecuteWatchAction extends WatcherRestHandler {
|
|||
|
||||
//This tightly binds the REST API to the java API
|
||||
private ExecuteWatchRequest parseRequest(RestRequest request, WatcherClient client) throws IOException {
|
||||
String watchId = request.param("id");
|
||||
ExecuteWatchRequestBuilder builder = client.prepareExecuteWatch(watchId);
|
||||
|
||||
ExecuteWatchRequestBuilder builder = client.prepareExecuteWatch();
|
||||
builder.setId(request.param("id"));
|
||||
if (request.content() == null || request.content().length() == 0) {
|
||||
return builder.request();
|
||||
}
|
||||
|
@ -79,13 +80,17 @@ public class RestExecuteWatchAction extends WatcherRestHandler {
|
|||
} else if (Field.RECORD_EXECUTION.match(currentFieldName)) {
|
||||
builder.setRecordExecution(parser.booleanValue());
|
||||
} else {
|
||||
throw new ParseException("could not parse watch execution request for [{}]. unexpected boolean field [{}]", watchId, currentFieldName);
|
||||
throw new ParseException("could not parse watch execution request. unexpected boolean field [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.ALTERNATIVE_INPUT.match(currentFieldName)) {
|
||||
builder.setAlternativeInput(parser.map());
|
||||
} else if (Field.TRIGGER_DATA.match(currentFieldName)) {
|
||||
builder.setTriggerData(parser.map());
|
||||
} else if (Field.WATCH.match(currentFieldName)) {
|
||||
XContentBuilder watcherSource = XContentBuilder.builder(parser.contentType().xContent());
|
||||
XContentHelper.copyCurrentStructure(watcherSource.generator(), parser);
|
||||
builder.setWatchSource(watcherSource.bytes());
|
||||
} else if (Field.ACTION_MODES.match(currentFieldName)) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
|
@ -95,17 +100,17 @@ public class RestExecuteWatchAction extends WatcherRestHandler {
|
|||
ActionExecutionMode mode = ActionExecutionMode.resolve(parser.textOrNull());
|
||||
builder.setActionMode(currentFieldName, mode);
|
||||
} catch (WatcherException we) {
|
||||
throw new ParseException("could not parse watch execution request for [{}].", watchId, we);
|
||||
throw new ParseException("could not parse watch execution request", we);
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("could not parse watch execution request for [{}]. unexpected array field [{}]", watchId, currentFieldName);
|
||||
throw new ParseException("could not parse watch execution request. unexpected array field [{}]", currentFieldName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("could not parse watch execution request for [{}]. unexpected object field [{}]", watchId, currentFieldName);
|
||||
throw new ParseException("could not parse watch execution request. unexpected object field [{}]", currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("could not parse watch execution request for [{}]. unexpected token [{}]", watchId, token);
|
||||
throw new ParseException("could not parse watch execution request. unexpected token [{}]", token);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,5 +137,6 @@ public class RestExecuteWatchAction extends WatcherRestHandler {
|
|||
ParseField ALTERNATIVE_INPUT = new ParseField("alternative_input");
|
||||
ParseField IGNORE_CONDITION = new ParseField("ignore_condition");
|
||||
ParseField TRIGGER_DATA = new ParseField("trigger_data");
|
||||
ParseField WATCH = new ParseField("watch");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.watcher.support.http.auth.ApplicableHttpAuth;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
|
||||
|
@ -57,6 +58,8 @@ public class HttpClient extends AbstractLifecycleComponent<HttpClient> {
|
|||
|
||||
private final HttpAuthRegistry httpAuthRegistry;
|
||||
private final Environment env;
|
||||
private final TimeValue defaultConnectionTimeout;
|
||||
private final TimeValue defaultReadTimeout;
|
||||
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
|
||||
|
@ -65,6 +68,8 @@ public class HttpClient extends AbstractLifecycleComponent<HttpClient> {
|
|||
super(settings);
|
||||
this.httpAuthRegistry = httpAuthRegistry;
|
||||
this.env = env;
|
||||
defaultConnectionTimeout = settings.getAsTime("watcher.http.default_connection_timeout", TimeValue.timeValueSeconds(10));
|
||||
defaultReadTimeout = settings.getAsTime("watcher.http.default_read_timeout", TimeValue.timeValueSeconds(10));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,6 +150,13 @@ public class HttpClient extends AbstractLifecycleComponent<HttpClient> {
|
|||
urlConnection.getOutputStream().write(bytes);
|
||||
urlConnection.getOutputStream().close();
|
||||
}
|
||||
|
||||
TimeValue connectionTimeout = request.connectionTimeout != null ? request.connectionTimeout : defaultConnectionTimeout;
|
||||
urlConnection.setConnectTimeout((int) connectionTimeout.millis());
|
||||
|
||||
TimeValue readTimeout = request.readTimeout != null ? request.readTimeout : defaultReadTimeout;
|
||||
urlConnection.setReadTimeout((int) readTimeout.millis());
|
||||
|
||||
urlConnection.connect();
|
||||
|
||||
final int statusCode = urlConnection.getResponseCode();
|
||||
|
|
|
@ -9,10 +9,12 @@ import com.google.common.collect.ImmutableMap;
|
|||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.WatcherDateTimeUtils;
|
||||
import org.elasticsearch.watcher.support.WatcherUtils;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuth;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
|
@ -31,10 +33,12 @@ public class HttpRequest implements ToXContent {
|
|||
final ImmutableMap<String, String> headers;
|
||||
final @Nullable HttpAuth auth;
|
||||
final @Nullable String body;
|
||||
final @Nullable TimeValue connectionTimeout;
|
||||
final @Nullable TimeValue readTimeout;
|
||||
|
||||
public HttpRequest(String host, int port, @Nullable Scheme scheme, @Nullable HttpMethod method, @Nullable String path,
|
||||
@Nullable ImmutableMap<String, String> params, @Nullable ImmutableMap<String, String> headers,
|
||||
@Nullable HttpAuth auth, @Nullable String body) {
|
||||
@Nullable HttpAuth auth, @Nullable String body, @Nullable TimeValue connectionTimeout, @Nullable TimeValue readTimeout) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.scheme = scheme != null ? scheme : Scheme.HTTP;
|
||||
|
@ -44,6 +48,8 @@ public class HttpRequest implements ToXContent {
|
|||
this.headers = headers != null ? headers : ImmutableMap.<String, String>of();
|
||||
this.auth = auth;
|
||||
this.body = body;
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
public Scheme scheme() {
|
||||
|
@ -86,6 +92,14 @@ public class HttpRequest implements ToXContent {
|
|||
return body;
|
||||
}
|
||||
|
||||
public TimeValue connectionTimeout() {
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
public TimeValue readTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
|
@ -108,6 +122,12 @@ public class HttpRequest implements ToXContent {
|
|||
if (body != null) {
|
||||
builder.field(Field.BODY.getPreferredName(), body);
|
||||
}
|
||||
if (connectionTimeout != null) {
|
||||
builder.field(Field.CONNECTION_TIMEOUT.getPreferredName(), connectionTimeout.toString());
|
||||
}
|
||||
if (readTimeout != null) {
|
||||
builder.field(Field.READ_TIMEOUT.getPreferredName(), readTimeout.toString());
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
|
@ -126,6 +146,8 @@ public class HttpRequest implements ToXContent {
|
|||
if (!params.equals(that.params)) return false;
|
||||
if (!headers.equals(that.headers)) return false;
|
||||
if (auth != null ? !auth.equals(that.auth) : that.auth != null) return false;
|
||||
if (connectionTimeout != null ? !connectionTimeout.equals(that.connectionTimeout) : that.connectionTimeout != null) return false;
|
||||
if (readTimeout != null ? !readTimeout.equals(that.readTimeout) : that.readTimeout != null) return false;
|
||||
return !(body != null ? !body.equals(that.body) : that.body != null);
|
||||
|
||||
}
|
||||
|
@ -140,6 +162,8 @@ public class HttpRequest implements ToXContent {
|
|||
result = 31 * result + params.hashCode();
|
||||
result = 31 * result + headers.hashCode();
|
||||
result = 31 * result + (auth != null ? auth.hashCode() : 0);
|
||||
result = 31 * result + (connectionTimeout != null ? connectionTimeout.hashCode() : 0);
|
||||
result = 31 * result + (readTimeout != null ? readTimeout.hashCode() : 0);
|
||||
result = 31 * result + (body != null ? body.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
@ -153,6 +177,8 @@ public class HttpRequest implements ToXContent {
|
|||
"], method=[" + method +
|
||||
"], port=[" + port +
|
||||
"], host=[" + host + '\'' +
|
||||
"], connection_timeout=[" + connectionTimeout + '\'' +
|
||||
"], read_timeout=[" + readTimeout + '\'' +
|
||||
"]}";
|
||||
}
|
||||
|
||||
|
@ -178,6 +204,18 @@ public class HttpRequest implements ToXContent {
|
|||
currentFieldName = parser.currentName();
|
||||
} else if (Field.AUTH.match(currentFieldName)) {
|
||||
builder.auth(httpAuthRegistry.parse(parser));
|
||||
} else if (Field.CONNECTION_TIMEOUT.match(currentFieldName)) {
|
||||
try {
|
||||
builder.connectionTimeout(WatcherDateTimeUtils.parseTimeValue(parser, null, Field.CONNECTION_TIMEOUT.toString()));
|
||||
} catch (WatcherDateTimeUtils.ParseException pe) {
|
||||
throw new ParseException("could not parse http request. invalid time value for [{}] field", pe, currentFieldName);
|
||||
}
|
||||
} else if (Field.READ_TIMEOUT.match(currentFieldName)) {
|
||||
try {
|
||||
builder.readTimeout(WatcherDateTimeUtils.parseTimeValue(parser, null, Field.READ_TIMEOUT.toString()));
|
||||
} catch (WatcherDateTimeUtils.ParseException pe) {
|
||||
throw new ParseException("could not parse http request. invalid time value for [{}] field", pe, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (Field.HEADERS.match(currentFieldName)) {
|
||||
builder.setHeaders((Map) WatcherUtils.flattenModel(parser.map()));
|
||||
|
@ -186,7 +224,7 @@ public class HttpRequest implements ToXContent {
|
|||
} else if (Field.BODY.match(currentFieldName)) {
|
||||
builder.body(parser.text());
|
||||
} else {
|
||||
throw new ParseException("could not parse http request. unexpected object field [" + currentFieldName + "]");
|
||||
throw new ParseException("could not parse http request. unexpected object field [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (Field.SCHEME.match(currentFieldName)) {
|
||||
|
@ -200,25 +238,25 @@ public class HttpRequest implements ToXContent {
|
|||
} else if (Field.BODY.match(currentFieldName)) {
|
||||
builder.body(parser.text());
|
||||
} else {
|
||||
throw new ParseException("could not parse http request. unexpected string field [" + currentFieldName + "]");
|
||||
throw new ParseException("could not parse http request. unexpected string field [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
if (Field.PORT.match(currentFieldName)) {
|
||||
builder.port = parser.intValue();
|
||||
} else {
|
||||
throw new ParseException("could not parse http request. unexpected numeric field [" + currentFieldName + "]");
|
||||
throw new ParseException("could not parse http request. unexpected numeric field [{}]", currentFieldName);
|
||||
}
|
||||
} else {
|
||||
throw new ParseException("could not parse http request. unexpected token [" + token + "]");
|
||||
throw new ParseException("could not parse http request. unexpected token [{}]", token);
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.host == null) {
|
||||
throw new ParseException("could not parse http request. missing required [host] field");
|
||||
throw new ParseException("could not parse http request. missing required [{}] field", Field.HOST.getPreferredName());
|
||||
}
|
||||
|
||||
if (builder.port < 0) {
|
||||
throw new ParseException("could not parse http request. missing required [port] field");
|
||||
throw new ParseException("could not parse http request. missing required [{}] field", Field.PORT.getPreferredName());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
@ -226,12 +264,12 @@ public class HttpRequest implements ToXContent {
|
|||
|
||||
public static class ParseException extends WatcherException {
|
||||
|
||||
public ParseException(String msg) {
|
||||
super(msg);
|
||||
public ParseException(String msg, Object... args) {
|
||||
super(msg, args);
|
||||
}
|
||||
|
||||
public ParseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
public ParseException(String msg, Throwable cause, Object... args) {
|
||||
super(msg, cause, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +285,8 @@ public class HttpRequest implements ToXContent {
|
|||
private ImmutableMap.Builder<String, String> headers = ImmutableMap.builder();
|
||||
private HttpAuth auth;
|
||||
private String body;
|
||||
private TimeValue connectionTimeout;
|
||||
private TimeValue readTimeout;
|
||||
|
||||
private Builder(String host, int port) {
|
||||
this.host = host;
|
||||
|
@ -301,13 +341,22 @@ public class HttpRequest implements ToXContent {
|
|||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
return new HttpRequest(host, port, scheme, method, path, params.build(), headers.build(), auth, body);
|
||||
public Builder connectionTimeout(TimeValue timeout) {
|
||||
this.connectionTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder readTimeout(TimeValue timeout) {
|
||||
this.readTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequest build() {
|
||||
return new HttpRequest(host, port, scheme, method, path, params.build(), headers.build(), auth, body, connectionTimeout, readTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
interface Field {
|
||||
public interface Field {
|
||||
ParseField SCHEME = new ParseField("scheme");
|
||||
ParseField HOST = new ParseField("host");
|
||||
ParseField PORT = new ParseField("port");
|
||||
|
@ -317,5 +366,7 @@ public class HttpRequest implements ToXContent {
|
|||
ParseField HEADERS = new ParseField("headers");
|
||||
ParseField AUTH = new ParseField("auth");
|
||||
ParseField BODY = new ParseField("body");
|
||||
ParseField CONNECTION_TIMEOUT = new ParseField("connection_timeout");
|
||||
ParseField READ_TIMEOUT = new ParseField("read_timeout");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,15 @@ package org.elasticsearch.watcher.support.http;
|
|||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.WatcherDateTimeUtils;
|
||||
import org.elasticsearch.watcher.support.http.HttpRequest.Field;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuth;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
|
@ -39,10 +41,12 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
private final ImmutableMap<String, Template> headers;
|
||||
private final HttpAuth auth;
|
||||
private final Template body;
|
||||
private final @Nullable TimeValue connectionTimeout;
|
||||
private final @Nullable TimeValue readTimeout;
|
||||
|
||||
public HttpRequestTemplate(String host, int port, @Nullable Scheme scheme, @Nullable HttpMethod method, @Nullable Template path,
|
||||
Map<String, Template> params, Map<String, Template> headers, HttpAuth auth,
|
||||
Template body) {
|
||||
Template body, @Nullable TimeValue connectionTimeout, @Nullable TimeValue readTimeout) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.scheme = scheme != null ? scheme :Scheme.HTTP;
|
||||
|
@ -52,6 +56,8 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
this.headers = headers != null ? ImmutableMap.copyOf(headers) : ImmutableMap.<String, Template>of();
|
||||
this.auth = auth;
|
||||
this.body = body;
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
public Scheme scheme() {
|
||||
|
@ -90,6 +96,14 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
return body;
|
||||
}
|
||||
|
||||
public TimeValue connectionTimeout() {
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
public TimeValue readTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
public HttpRequest render(TemplateEngine engine, Map<String, Object> model) {
|
||||
HttpRequest.Builder request = HttpRequest.builder(host, port);
|
||||
request.method(method);
|
||||
|
@ -123,39 +137,51 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
if (body != null) {
|
||||
request.body(engine.render(body, model));
|
||||
}
|
||||
if (connectionTimeout != null) {
|
||||
request.connectionTimeout(connectionTimeout);
|
||||
}
|
||||
if (readTimeout != null) {
|
||||
request.readTimeout(readTimeout);
|
||||
}
|
||||
return request.build();
|
||||
}
|
||||
|
||||
public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Parser.SCHEME_FIELD.getPreferredName(), scheme, params);
|
||||
builder.field(Parser.HOST_FIELD.getPreferredName(), host);
|
||||
builder.field(Parser.PORT_FIELD.getPreferredName(), port);
|
||||
builder.field(Parser.METHOD_FIELD.getPreferredName(), method, params);
|
||||
builder.field(Field.SCHEME.getPreferredName(), scheme, params);
|
||||
builder.field(Field.HOST.getPreferredName(), host);
|
||||
builder.field(Field.PORT.getPreferredName(), port);
|
||||
builder.field(Field.METHOD.getPreferredName(), method, params);
|
||||
if (path != null) {
|
||||
builder.field(Parser.PATH_FIELD.getPreferredName(), path, params);
|
||||
builder.field(Field.PATH.getPreferredName(), path, params);
|
||||
}
|
||||
if (this.params != null) {
|
||||
builder.startObject(Parser.PARAMS_FIELD.getPreferredName());
|
||||
builder.startObject(Field.PARAMS.getPreferredName());
|
||||
for (Map.Entry<String, Template> entry : this.params.entrySet()) {
|
||||
builder.field(entry.getKey(), entry.getValue(), params);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
if (headers != null) {
|
||||
builder.startObject(Parser.HEADERS_FIELD.getPreferredName());
|
||||
builder.startObject(Field.HEADERS.getPreferredName());
|
||||
for (Map.Entry<String, Template> entry : headers.entrySet()) {
|
||||
builder.field(entry.getKey(), entry.getValue(), params);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
if (auth != null) {
|
||||
builder.startObject(Parser.AUTH_FIELD.getPreferredName())
|
||||
builder.startObject(Field.AUTH.getPreferredName())
|
||||
.field(auth.type(), auth, params)
|
||||
.endObject();
|
||||
}
|
||||
if (body != null) {
|
||||
builder.field(Parser.BODY_FIELD.getPreferredName(), body, params);
|
||||
builder.field(Field.BODY.getPreferredName(), body, params);
|
||||
}
|
||||
if (connectionTimeout != null) {
|
||||
builder.field(Field.CONNECTION_TIMEOUT.getPreferredName(), connectionTimeout.toString());
|
||||
}
|
||||
if (readTimeout != null) {
|
||||
builder.field(Field.READ_TIMEOUT.getPreferredName(), readTimeout.toString());
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
@ -175,6 +201,8 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
if (params != null ? !params.equals(that.params) : that.params != null) return false;
|
||||
if (headers != null ? !headers.equals(that.headers) : that.headers != null) return false;
|
||||
if (auth != null ? !auth.equals(that.auth) : that.auth != null) return false;
|
||||
if (connectionTimeout != null ? !connectionTimeout.equals(that.connectionTimeout) : that.connectionTimeout != null) return false;
|
||||
if (readTimeout != null ? !readTimeout.equals(that.readTimeout) : that.readTimeout != null) return false;
|
||||
return body != null ? body.equals(that.body) : that.body == null;
|
||||
}
|
||||
|
||||
|
@ -189,6 +217,8 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
result = 31 * result + (headers != null ? headers.hashCode() : 0);
|
||||
result = 31 * result + (auth != null ? auth.hashCode() : 0);
|
||||
result = 31 * result + (body != null ? body.hashCode() : 0);
|
||||
result = 31 * result + (connectionTimeout != null ? connectionTimeout.hashCode() : 0);
|
||||
result = 31 * result + (readTimeout != null ? readTimeout.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -198,17 +228,6 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
|
||||
public static class Parser {
|
||||
|
||||
public static final ParseField SCHEME_FIELD = new ParseField("scheme");
|
||||
public static final ParseField HOST_FIELD = new ParseField("host");
|
||||
public static final ParseField PORT_FIELD = new ParseField("port");
|
||||
public static final ParseField METHOD_FIELD = new ParseField("method");
|
||||
public static final ParseField PATH_FIELD = new ParseField("path");
|
||||
public static final ParseField PARAMS_FIELD = new ParseField("params");
|
||||
public static final ParseField HEADERS_FIELD = new ParseField("headers");
|
||||
public static final ParseField AUTH_FIELD = new ParseField("auth");
|
||||
public static final ParseField BODY_FIELD = new ParseField("body");
|
||||
public static final ParseField XBODY_FIELD = new ParseField("xbody");
|
||||
|
||||
private final HttpAuthRegistry httpAuthRegistry;
|
||||
|
||||
@Inject
|
||||
|
@ -225,32 +244,44 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (PATH_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.PATH.match(currentFieldName)) {
|
||||
builder.path(parseFieldTemplate(currentFieldName, parser));
|
||||
} else if (HEADERS_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.HEADERS.match(currentFieldName)) {
|
||||
builder.putHeaders(parseFieldTemplates(currentFieldName, parser));
|
||||
} else if (PARAMS_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.PARAMS.match(currentFieldName)) {
|
||||
builder.putParams(parseFieldTemplates(currentFieldName, parser));
|
||||
} else if (BODY_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.BODY.match(currentFieldName)) {
|
||||
builder.body(parseFieldTemplate(currentFieldName, parser));
|
||||
} else if (Field.CONNECTION_TIMEOUT.match(currentFieldName)) {
|
||||
try {
|
||||
builder.connectionTimeout(WatcherDateTimeUtils.parseTimeValue(parser, null, Field.CONNECTION_TIMEOUT.toString()));
|
||||
} catch (WatcherDateTimeUtils.ParseException pe) {
|
||||
throw new ParseException("could not parse http request template. invalid time value for [{}] field", pe, currentFieldName);
|
||||
}
|
||||
} else if (Field.READ_TIMEOUT.match(currentFieldName)) {
|
||||
try {
|
||||
builder.readTimeout(WatcherDateTimeUtils.parseTimeValue(parser, null, Field.READ_TIMEOUT.toString()));
|
||||
} catch (WatcherDateTimeUtils.ParseException pe) {
|
||||
throw new ParseException("could not parse http request template. invalid time value for [{}] field", pe, currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (AUTH_FIELD.match(currentFieldName)) {
|
||||
if (Field.AUTH.match(currentFieldName)) {
|
||||
builder.auth(httpAuthRegistry.parse(parser));
|
||||
} else {
|
||||
throw new ParseException("could not parse http request template. unexpected object field [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if (SCHEME_FIELD.match(currentFieldName)) {
|
||||
if (Field.SCHEME.match(currentFieldName)) {
|
||||
builder.scheme(Scheme.parse(parser.text()));
|
||||
} else if (METHOD_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.METHOD.match(currentFieldName)) {
|
||||
builder.method(HttpMethod.parse(parser.text()));
|
||||
} else if (HOST_FIELD.match(currentFieldName)) {
|
||||
} else if (Field.HOST.match(currentFieldName)) {
|
||||
builder.host = parser.text();
|
||||
} else {
|
||||
throw new ParseException("could not parse http request template. unexpected string field [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
if (PORT_FIELD.match(currentFieldName)) {
|
||||
if (Field.PORT.match(currentFieldName)) {
|
||||
builder.port = parser.intValue();
|
||||
} else {
|
||||
throw new ParseException("could not parse http request template. unexpected numeric field [{}]", currentFieldName);
|
||||
|
@ -261,10 +292,10 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
}
|
||||
|
||||
if (builder.host == null) {
|
||||
throw new ParseException("could not parse http request template. missing required [{}] string field", HOST_FIELD.getPreferredName());
|
||||
throw new ParseException("could not parse http request template. missing required [{}] string field", Field.HOST.getPreferredName());
|
||||
}
|
||||
if (builder.port <= 0) {
|
||||
throw new ParseException("could not parse http request template. missing required [{}] numeric field", PORT_FIELD.getPreferredName());
|
||||
throw new ParseException("could not parse http request template. missing required [{}] numeric field", Field.PORT.getPreferredName());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
@ -317,6 +348,8 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
private final ImmutableMap.Builder<String, Template> headers = ImmutableMap.builder();
|
||||
private HttpAuth auth;
|
||||
private Template body;
|
||||
private TimeValue connectionTimeout;
|
||||
private TimeValue readTimeout;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
@ -407,8 +440,18 @@ public class HttpRequestTemplate implements ToXContent {
|
|||
return body(Template.inline(content));
|
||||
}
|
||||
|
||||
public Builder connectionTimeout(TimeValue timeout) {
|
||||
this.connectionTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder readTimeout(TimeValue timeout) {
|
||||
this.readTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpRequestTemplate build() {
|
||||
return new HttpRequestTemplate(host, port, scheme, method, path, params.build(), headers.build(), auth, body);
|
||||
return new HttpRequestTemplate(host, port, scheme, method, path, params.build(), headers.build(), auth, body, connectionTimeout, readTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ public abstract class ExecutableTransform<T extends Transform, R extends Transfo
|
|||
return transform;
|
||||
}
|
||||
|
||||
public abstract R execute(WatchExecutionContext ctx, Payload payload) throws IOException;
|
||||
public abstract R execute(WatchExecutionContext ctx, Payload payload);
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.transform;
|
||||
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -21,27 +23,64 @@ public interface Transform extends ToXContent {
|
|||
|
||||
abstract class Result implements ToXContent {
|
||||
|
||||
public enum Status {
|
||||
SUCCESS, FAILURE
|
||||
}
|
||||
|
||||
protected final String type;
|
||||
protected final Payload payload;
|
||||
protected final Status status;
|
||||
protected final @Nullable Payload payload;
|
||||
protected final @Nullable String reason;
|
||||
|
||||
public Result(String type, Payload payload) {
|
||||
this.type = type;
|
||||
this.status = Status.SUCCESS;
|
||||
this.payload = payload;
|
||||
this.reason = null;
|
||||
}
|
||||
|
||||
public Result(String type, Exception e) {
|
||||
this.type = type;
|
||||
this.status = Status.FAILURE;
|
||||
this.reason = ExceptionsHelper.detailedMessage(e);
|
||||
this.payload = null;
|
||||
}
|
||||
|
||||
public String type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Status status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Payload payload() {
|
||||
assert status == Status.SUCCESS;
|
||||
return payload;
|
||||
}
|
||||
|
||||
public String reason() {
|
||||
assert status == Status.FAILURE;
|
||||
return reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(Field.TYPE.getPreferredName(), type);
|
||||
builder.field(Field.PAYLOAD.getPreferredName(), payload, params);
|
||||
builder.field(Field.STATUS.getPreferredName(), status);
|
||||
switch (status) {
|
||||
case SUCCESS:
|
||||
assert reason == null;
|
||||
builder.field(Field.PAYLOAD.getPreferredName(), payload, params);
|
||||
break;
|
||||
case FAILURE:
|
||||
assert payload == null;
|
||||
builder.field(Field.REASON.getPreferredName(), reason);
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
typeXContent(builder, params);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
@ -56,8 +95,12 @@ public interface Transform extends ToXContent {
|
|||
}
|
||||
|
||||
interface Field {
|
||||
ParseField TYPE = new ParseField("type");
|
||||
ParseField PAYLOAD = new ParseField("payload");
|
||||
ParseField TRANSFORM = new ParseField("transform");
|
||||
|
||||
ParseField TYPE = new ParseField("type");
|
||||
ParseField STATUS = new ParseField("status");
|
||||
ParseField PAYLOAD = new ParseField("payload");
|
||||
ParseField REASON = new ParseField("reason");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,19 +101,27 @@ public class ChainTransform implements Transform {
|
|||
this.results = results;
|
||||
}
|
||||
|
||||
public Result(Exception e, ImmutableList<Transform.Result> results) {
|
||||
super(TYPE, e);
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public ImmutableList<Transform.Result> results() {
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(type);
|
||||
builder.startArray(Field.RESULTS.getPreferredName());
|
||||
for (Transform.Result result : results) {
|
||||
result.toXContent(builder, params);
|
||||
if (!results.isEmpty()) {
|
||||
builder.startObject(type);
|
||||
builder.startArray(Field.RESULTS.getPreferredName());
|
||||
for (Transform.Result result : results) {
|
||||
result.toXContent(builder, params);
|
||||
}
|
||||
builder.endArray();
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endArray();
|
||||
return builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,11 +32,24 @@ public class ExecutableChainTransform extends ExecutableTransform<ChainTransform
|
|||
}
|
||||
|
||||
@Override
|
||||
public ChainTransform.Result execute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
public ChainTransform.Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
ImmutableList.Builder<Transform.Result> results = ImmutableList.builder();
|
||||
try {
|
||||
return doExecute(ctx, payload, results);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to execute [{}] transform for [{}]", e, ChainTransform.TYPE, ctx.id());
|
||||
return new ChainTransform.Result(e, results.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ChainTransform.Result doExecute(WatchExecutionContext ctx, Payload payload, ImmutableList.Builder<Transform.Result> results) throws IOException {
|
||||
for (ExecutableTransform transform : transforms) {
|
||||
Transform.Result result = transform.execute(ctx, payload);
|
||||
results.add(result);
|
||||
if (result.status() == Transform.Result.Status.FAILURE) {
|
||||
throw new ChainTransformException("failed to execute [{}] transform. failed to execute sub-transform [{}]", ChainTransform.TYPE, transform.type());
|
||||
}
|
||||
payload = result.payload();
|
||||
}
|
||||
return new ChainTransform.Result(payload, results.build());
|
||||
|
|
|
@ -40,7 +40,17 @@ public class ExecutableScriptTransform extends ExecutableTransform<ScriptTransfo
|
|||
}
|
||||
|
||||
@Override
|
||||
public ScriptTransform.Result execute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
public ScriptTransform.Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
try {
|
||||
return doExecute(ctx, payload);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to execute [{}] transform for [{}]", e, ScriptTransform.TYPE, ctx.id());
|
||||
return new ScriptTransform.Result(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ScriptTransform.Result doExecute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
Script script = transform.getScript();
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.putAll(script.params());
|
||||
|
|
|
@ -74,6 +74,10 @@ public class ScriptTransform implements Transform {
|
|||
super(TYPE, payload);
|
||||
}
|
||||
|
||||
public Result(Exception e) {
|
||||
super(TYPE, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
|
|
|
@ -15,8 +15,6 @@ import org.elasticsearch.watcher.support.init.proxy.ClientProxy;
|
|||
import org.elasticsearch.watcher.transform.ExecutableTransform;
|
||||
import org.elasticsearch.watcher.watch.Payload;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -32,10 +30,16 @@ public class ExecutableSearchTransform extends ExecutableTransform<SearchTransfo
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchTransform.Result execute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
SearchRequest req = WatcherUtils.createSearchRequestFromPrototype(transform.request, ctx, payload);
|
||||
SearchResponse resp = client.search(req);
|
||||
return new SearchTransform.Result(req, new Payload.XContent(resp));
|
||||
public SearchTransform.Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
SearchRequest request = null;
|
||||
try {
|
||||
request = WatcherUtils.createSearchRequestFromPrototype(transform.request, ctx, payload);
|
||||
SearchResponse resp = client.search(request);
|
||||
return new SearchTransform.Result(request, new Payload.XContent(resp));
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to execute [{}] transform for [{}]", e, SearchTransform.TYPE, ctx.id());
|
||||
return new SearchTransform.Result(request, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.watcher.transform.search;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -74,23 +75,31 @@ public class SearchTransform implements Transform {
|
|||
|
||||
public static class Result extends Transform.Result {
|
||||
|
||||
private final SearchRequest request;
|
||||
private final @Nullable SearchRequest request;
|
||||
|
||||
public Result(SearchRequest request, Payload payload) {
|
||||
super(TYPE, payload);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public Result(SearchRequest request, Exception e) {
|
||||
super(TYPE, e);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public SearchRequest executedRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(type);
|
||||
builder.field(Field.REQUEST.getPreferredName());
|
||||
WatcherUtils.writeSearchRequest(request, builder, params);
|
||||
return builder.endObject();
|
||||
if (request != null) {
|
||||
builder.startObject(type);
|
||||
builder.field(Field.REQUEST.getPreferredName());
|
||||
WatcherUtils.writeSearchRequest(request, builder, params);
|
||||
builder.endObject();
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,11 @@ import org.elasticsearch.action.ActionRequestValidationException;
|
|||
import org.elasticsearch.action.ValidateActions;
|
||||
import org.elasticsearch.action.support.master.MasterNodeReadRequest;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.watcher.execution.ActionExecutionMode;
|
||||
import org.elasticsearch.watcher.support.validation.Validation;
|
||||
import org.elasticsearch.watcher.trigger.TriggerEvent;
|
||||
|
@ -24,12 +27,15 @@ import java.util.Map;
|
|||
*/
|
||||
public class ExecuteWatchRequest extends MasterNodeReadRequest<ExecuteWatchRequest> {
|
||||
|
||||
public static final String INLINE_WATCH_ID = "_inlined_";
|
||||
|
||||
private String id;
|
||||
private boolean ignoreCondition = false;
|
||||
private boolean recordExecution = false;
|
||||
private @Nullable Map<String, Object> triggerData = null;
|
||||
private @Nullable Map<String, Object> alternativeInput = null;
|
||||
private Map<String, ActionExecutionMode> actionModes = new HashMap<>();
|
||||
private BytesReference watchSource;
|
||||
|
||||
ExecuteWatchRequest() {
|
||||
}
|
||||
|
@ -120,6 +126,26 @@ public class ExecuteWatchRequest extends MasterNodeReadRequest<ExecuteWatchReque
|
|||
return triggerData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the source of the watch to execute
|
||||
*/
|
||||
public BytesReference getWatchSource() {
|
||||
return watchSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param watchSource instead of using an existing watch use this non persisted watch
|
||||
*/
|
||||
public void setWatchSource(BytesReference watchSource) {
|
||||
this.watchSource = watchSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param watchSource instead of using an existing watch use this non persisted watch
|
||||
*/
|
||||
public void setWatchSource(WatchSourceBuilder watchSource) {
|
||||
this.watchSource = watchSource.buildAsBytes(XContentType.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -143,26 +169,34 @@ public class ExecuteWatchRequest extends MasterNodeReadRequest<ExecuteWatchReque
|
|||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
if (id == null){
|
||||
if (id == null && watchSource == null){
|
||||
validationException = ValidateActions.addValidationError("watch id is missing", validationException);
|
||||
}
|
||||
Validation.Error error = Validation.watchId(id);
|
||||
if (error != null) {
|
||||
validationException = ValidateActions.addValidationError(error.message(), validationException);
|
||||
}
|
||||
for (Map.Entry<String, ActionExecutionMode> modes : actionModes.entrySet()) {
|
||||
error = Validation.actionId(modes.getKey());
|
||||
if (id != null) {
|
||||
Validation.Error error = Validation.watchId(id);
|
||||
if (error != null) {
|
||||
validationException = ValidateActions.addValidationError(error.message(), validationException);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, ActionExecutionMode> modes : actionModes.entrySet()) {
|
||||
Validation.Error error = Validation.actionId(modes.getKey());
|
||||
if (error != null) {
|
||||
validationException = ValidateActions.addValidationError(error.message(), validationException);
|
||||
}
|
||||
}
|
||||
if (watchSource != null && id != null) {
|
||||
validationException = ValidateActions.addValidationError("a watch execution request must either have a watch id or an inline watch source but not both", validationException);
|
||||
}
|
||||
if (watchSource != null && recordExecution) {
|
||||
validationException = ValidateActions.addValidationError("the execution of an inline watch cannot be recorded", validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
id = in.readString();
|
||||
id = in.readOptionalString();
|
||||
ignoreCondition = in.readBoolean();
|
||||
recordExecution = in.readBoolean();
|
||||
if (in.readBoolean()){
|
||||
|
@ -176,13 +210,16 @@ public class ExecuteWatchRequest extends MasterNodeReadRequest<ExecuteWatchReque
|
|||
for (int i = 0; i < actionModesCount; i++) {
|
||||
actionModes.put(in.readString(), ActionExecutionMode.resolve(in.readByte()));
|
||||
}
|
||||
if (in.readBoolean()) {
|
||||
watchSource = in.readBytesReference();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeString(id);
|
||||
out.writeOptionalString(id);
|
||||
out.writeBoolean(ignoreCondition);
|
||||
out.writeBoolean(recordExecution);
|
||||
out.writeBoolean(alternativeInput != null);
|
||||
|
@ -198,6 +235,10 @@ public class ExecuteWatchRequest extends MasterNodeReadRequest<ExecuteWatchReque
|
|||
out.writeString(entry.getKey());
|
||||
out.writeByte(entry.getValue().id());
|
||||
}
|
||||
out.writeBoolean(watchSource != null);
|
||||
if (watchSource != null) {
|
||||
out.writeBytesReference(watchSource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.transport.actions.execute;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
import org.elasticsearch.watcher.client.WatcherClient;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.watcher.client.WatchSourceBuilder;
|
||||
import org.elasticsearch.watcher.execution.ActionExecutionMode;
|
||||
import org.elasticsearch.watcher.trigger.TriggerEvent;
|
||||
|
||||
|
@ -76,6 +75,22 @@ public class ExecuteWatchRequestBuilder extends MasterNodeOperationRequestBuilde
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param watchSource instead of using an existing watch use this non persisted watch
|
||||
*/
|
||||
public ExecuteWatchRequestBuilder setWatchSource(BytesReference watchSource) {
|
||||
request.setWatchSource(watchSource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param watchSource instead of using an existing watch use this non persisted watch
|
||||
*/
|
||||
public ExecuteWatchRequestBuilder setWatchSource(WatchSourceBuilder watchSource) {
|
||||
request.setWatchSource(watchSource);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode in which the given action (identified by its id) will be handled.
|
||||
*
|
||||
|
@ -86,5 +101,4 @@ public class ExecuteWatchRequestBuilder extends MasterNodeOperationRequestBuilde
|
|||
request.setActionMode(actionId, actionMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,16 +49,19 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
private final WatchStore watchStore;
|
||||
private final Clock clock;
|
||||
private final TriggerService triggerService;
|
||||
private final Watch.Parser watchParser;
|
||||
|
||||
@Inject
|
||||
public TransportExecuteWatchAction(Settings settings, TransportService transportService, ClusterService clusterService,
|
||||
ThreadPool threadPool, ActionFilters actionFilters, ExecutionService executionService,
|
||||
Clock clock, LicenseService licenseService, WatchStore watchStore, TriggerService triggerService) {
|
||||
Clock clock, LicenseService licenseService, WatchStore watchStore, TriggerService triggerService,
|
||||
Watch.Parser watchParser) {
|
||||
super(settings, ExecuteWatchAction.NAME, transportService, clusterService, threadPool, actionFilters, licenseService, ExecuteWatchRequest.class);
|
||||
this.executionService = executionService;
|
||||
this.watchStore = watchStore;
|
||||
this.clock = clock;
|
||||
this.triggerService = triggerService;
|
||||
this.watchParser = watchParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,15 +77,26 @@ public class TransportExecuteWatchAction extends WatcherTransportAction<ExecuteW
|
|||
@Override
|
||||
protected void masterOperation(ExecuteWatchRequest request, ClusterState state, ActionListener<ExecuteWatchResponse> listener) throws ElasticsearchException {
|
||||
try {
|
||||
Watch watch = watchStore.get(request.getId());
|
||||
if (watch == null) {
|
||||
throw new WatcherException("watch [{}] does not exist", request.getId());
|
||||
Watch watch;
|
||||
boolean knownWatch;
|
||||
if (request.getId() != null) {
|
||||
watch = watchStore.get(request.getId());
|
||||
if (watch == null) {
|
||||
throw new WatcherException("watch [{}] does not exist", request.getId());
|
||||
}
|
||||
knownWatch = true;
|
||||
} else if (request.getWatchSource() != null) {
|
||||
assert !request.isRecordExecution();
|
||||
watch = watchParser.parse(ExecuteWatchRequest.INLINE_WATCH_ID, false, request.getWatchSource());
|
||||
knownWatch = false;
|
||||
} else {
|
||||
throw new WatcherException("no watch provided");
|
||||
}
|
||||
|
||||
String triggerType = watch.trigger().type();
|
||||
TriggerEvent triggerEvent = triggerService.simulateEvent(triggerType, watch.id(), request.getTriggerData());
|
||||
|
||||
ManualExecutionContext.Builder ctxBuilder = ManualExecutionContext.builder(watch, true, new ManualTriggerEvent(triggerEvent.jobName(), triggerEvent), executionService.defaultThrottlePeriod());
|
||||
ManualExecutionContext.Builder ctxBuilder = ManualExecutionContext.builder(watch, knownWatch, new ManualTriggerEvent(triggerEvent.jobName(), triggerEvent), executionService.defaultThrottlePeriod());
|
||||
|
||||
DateTime executionTime = clock.now(DateTimeZone.UTC);
|
||||
ctxBuilder.executionTime(executionTime);
|
||||
|
|
|
@ -105,7 +105,9 @@ public class TriggerService extends AbstractComponent {
|
|||
|
||||
public Trigger parseTrigger(String jobName, String type, XContentParser parser) throws IOException {
|
||||
TriggerEngine engine = engines.get(type);
|
||||
assert engine != null;
|
||||
if (engine == null) {
|
||||
throw new TriggerException("could not parse trigger [{}] for [{}]. unknown trigger type [{}]", type, jobName, type);
|
||||
}
|
||||
return engine.parseTrigger(jobName, parser);
|
||||
}
|
||||
|
||||
|
|
|
@ -252,6 +252,7 @@ public class Watch implements TriggerEngine.Job, ToXContent {
|
|||
if (withSecrets) {
|
||||
parser = new SensitiveXContentParser(parser, secretService);
|
||||
}
|
||||
parser.nextToken();
|
||||
return parse(id, includeStatus, parser);
|
||||
} catch (IOException ioe) {
|
||||
throw new WatcherException("could not parse watch [{}]", ioe, id);
|
||||
|
@ -273,7 +274,7 @@ public class Watch implements TriggerEngine.Job, ToXContent {
|
|||
WatchStatus status = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == null ) {
|
||||
throw new ParseException("could not parse watch [{}]. null token", id);
|
||||
|
|
|
@ -108,14 +108,14 @@
|
|||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"payload" : {
|
||||
"type" : "object",
|
||||
"enabled" : false
|
||||
},
|
||||
"status" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"payload" : {
|
||||
"type" : "object",
|
||||
"enabled" : false
|
||||
},
|
||||
"search": {
|
||||
"type": "object",
|
||||
"dynamic": true,
|
||||
|
@ -170,6 +170,10 @@
|
|||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"status" : {
|
||||
"type" : "string",
|
||||
"index" : "not_analyzed"
|
||||
},
|
||||
"met" : {
|
||||
"type" : "boolean"
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.email;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
|
@ -57,6 +56,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
}
|
||||
};
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
|
||||
EmailTemplate.Builder emailBuilder = EmailTemplate.builder();
|
||||
Template subject = null;
|
||||
|
@ -72,7 +72,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
Template htmlBody = null;
|
||||
if (randomBoolean()) {
|
||||
htmlBody = Template.inline("_html_body").build();
|
||||
emailBuilder.htmlBody(htmlBody, true);
|
||||
emailBuilder.htmlBody(htmlBody);
|
||||
}
|
||||
EmailTemplate email = emailBuilder.build();
|
||||
|
||||
|
@ -82,7 +82,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
DataAttachment dataAttachment = randomDataAttachment();
|
||||
|
||||
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
Payload payload = new Payload.Simple(data);
|
||||
|
@ -120,6 +120,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
when(engine.render(textBody, expectedModel)).thenReturn(textBody.getTemplate());
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
when(htmlSanitizer.sanitize(htmlBody.getTemplate())).thenReturn(htmlBody.getTemplate());
|
||||
when(engine.render(htmlBody, expectedModel)).thenReturn(htmlBody.getTemplate());
|
||||
}
|
||||
|
||||
|
@ -142,6 +143,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
@Test
|
||||
public void testParser() throws Exception {
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
EmailService emailService = mock(EmailService.class);
|
||||
Profile profile = randomFrom(Profile.values());
|
||||
Email.Priority priority = randomFrom(Email.Priority.values());
|
||||
|
@ -241,7 +243,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
ExecutableEmailAction executable = new EmailActionFactory(Settings.EMPTY, emailService, engine)
|
||||
ExecutableEmailAction executable = new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(8), randomAsciiOfLength(3), parser);
|
||||
|
||||
assertThat(executable, notNullValue());
|
||||
|
@ -289,6 +291,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
public void testParser_SelfGenerated() throws Exception {
|
||||
EmailService service = mock(EmailService.class);
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
EmailTemplate.Builder emailTemplate = EmailTemplate.builder();
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.from("from@domain");
|
||||
|
@ -312,7 +315,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
emailTemplate.textBody("_text_body");
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.htmlBody("_html_body", randomBoolean());
|
||||
emailTemplate.htmlBody("_html_body");
|
||||
}
|
||||
EmailTemplate email = emailTemplate.build();
|
||||
Authentication auth = randomBoolean() ? null : new Authentication("_user", new Secret("_passwd".toCharArray()));
|
||||
|
@ -321,7 +324,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
DataAttachment dataAttachment = randomDataAttachment();
|
||||
|
||||
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer);
|
||||
|
||||
boolean hideSecrets = randomBoolean();
|
||||
ToXContent.Params params = WatcherParams.builder().hideSecrets(hideSecrets).build();
|
||||
|
@ -332,7 +335,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
logger.info(bytes.toUtf8());
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
ExecutableEmailAction parsed = new EmailActionFactory(Settings.EMPTY, service, engine)
|
||||
ExecutableEmailAction parsed = new EmailActionFactory(Settings.EMPTY, service, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(4), randomAsciiOfLength(10), parser);
|
||||
|
||||
if (!hideSecrets) {
|
||||
|
@ -357,10 +360,11 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
public void testParser_Invalid() throws Exception {
|
||||
EmailService emailService = mock(EmailService.class);
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
XContentBuilder builder = jsonBuilder().startObject().field("unknown_field", "value");
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
new EmailActionFactory(Settings.EMPTY, emailService, engine)
|
||||
new EmailActionFactory(Settings.EMPTY, emailService, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.email.service;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
|
@ -43,25 +42,18 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
Template[] cc = randomFrom(possibleList, null);
|
||||
Template[] bcc = randomFrom(possibleList, null);
|
||||
Template priority = Template.inline(randomFrom(Email.Priority.values()).name()).build();
|
||||
boolean sanitizeHtml = randomBoolean();
|
||||
|
||||
Template templatedSubject = Template.inline("Templated Subject {{foo}}").build();
|
||||
String renderedTemplatedSubject = "Templated Subject bar";
|
||||
Template subjectTemplate = Template.inline("Templated Subject {{foo}}").build();
|
||||
String subject = "Templated Subject bar";
|
||||
|
||||
Template templatedBody = Template.inline("Templated Body {{foo}}").build();
|
||||
String renderedTemplatedBody = "Templated Body bar";
|
||||
Template textBodyTemplate = Template.inline("Templated Body {{foo}}").build();
|
||||
String textBody = "Templated Body bar";
|
||||
|
||||
Template templatedHtmlBodyGood = Template.inline("Templated Html Body <hr />").build();
|
||||
String renderedTemplatedHtmlBodyGood = "Templated Html Body <hr /> bar";
|
||||
Template htmlBodyTemplate = Template.inline("Templated Html Body <script>nefarious scripting</script>").build();
|
||||
String htmlBody = "Templated Html Body <script>nefarious scripting</script>";
|
||||
String sanitizedHtmlBody = "Templated Html Body";
|
||||
|
||||
Template templatedHtmlBodyBad = Template.inline("Templated Html Body <script>nefarious scripting</script>").build();
|
||||
String renderedTemplatedHtmlBodyBad = "Templated Html Body<script>nefarious scripting</script>";
|
||||
String renderedSanitizedHtmlBodyBad = "Templated Html Body";
|
||||
|
||||
Template htmlBody = randomFrom(templatedHtmlBodyGood, templatedHtmlBodyBad);
|
||||
|
||||
EmailTemplate emailTemplate = new EmailTemplate(from, replyTo, priority, to, cc, bcc,
|
||||
templatedSubject, templatedBody, htmlBody, sanitizeHtml);
|
||||
EmailTemplate emailTemplate = new EmailTemplate(from, replyTo, priority, to, cc, bcc, subjectTemplate, textBodyTemplate, htmlBodyTemplate);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
emailTemplate.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
@ -69,7 +61,7 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
EmailTemplate.Parser emailTemplateParser = new EmailTemplate.Parser(sanitizeHtml);
|
||||
EmailTemplate.Parser emailTemplateParser = new EmailTemplate.Parser();
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
@ -83,11 +75,14 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
EmailTemplate parsedEmailTemplate = emailTemplateParser.parsedTemplate();
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
when(htmlSanitizer.sanitize(htmlBody)).thenReturn(sanitizedHtmlBody);
|
||||
|
||||
TemplateEngine templateEngine = mock(TemplateEngine.class);
|
||||
when(templateEngine.render(templatedSubject, model)).thenReturn(renderedTemplatedSubject);
|
||||
when(templateEngine.render(templatedBody, model)).thenReturn(renderedTemplatedBody);
|
||||
when(templateEngine.render(templatedHtmlBodyGood, model)).thenReturn(renderedTemplatedHtmlBodyGood);
|
||||
when(templateEngine.render(templatedHtmlBodyBad, model)).thenReturn(renderedTemplatedHtmlBodyBad);
|
||||
when(templateEngine.render(subjectTemplate, model)).thenReturn(subject);
|
||||
when(templateEngine.render(textBodyTemplate, model)).thenReturn(textBody);
|
||||
when(templateEngine.render(htmlBodyTemplate, model)).thenReturn(htmlBody);
|
||||
for (Template possibleAddress : possibleList) {
|
||||
when(templateEngine.render(possibleAddress, model)).thenReturn(possibleAddress.getTemplate());
|
||||
}
|
||||
|
@ -96,7 +91,7 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
}
|
||||
when(templateEngine.render(priority, model)).thenReturn(priority.getTemplate());
|
||||
|
||||
Email.Builder emailBuilder = parsedEmailTemplate.render(templateEngine, model, new HashMap<String, Attachment>());
|
||||
Email.Builder emailBuilder = parsedEmailTemplate.render(templateEngine, model, htmlSanitizer, new HashMap<String, Attachment>());
|
||||
|
||||
assertThat(emailTemplate.from, equalTo(parsedEmailTemplate.from));
|
||||
assertThat(emailTemplate.replyTo, equalTo(parsedEmailTemplate.replyTo));
|
||||
|
@ -110,17 +105,9 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
|
||||
emailBuilder.id("_id");
|
||||
Email email = emailBuilder.build();
|
||||
assertThat(email.subject, equalTo(renderedTemplatedSubject));
|
||||
assertThat(email.textBody, equalTo(renderedTemplatedBody));
|
||||
if (htmlBody.equals(templatedHtmlBodyBad)) {
|
||||
if (sanitizeHtml) {
|
||||
assertThat(email.htmlBody, equalTo(renderedSanitizedHtmlBodyBad));
|
||||
} else {
|
||||
assertThat(email.htmlBody, equalTo(renderedTemplatedHtmlBodyBad));
|
||||
}
|
||||
} else {
|
||||
assertThat(email.htmlBody, equalTo(renderedTemplatedHtmlBodyGood));
|
||||
}
|
||||
assertThat(email.subject, equalTo(subject));
|
||||
assertThat(email.textBody, equalTo(textBody));
|
||||
assertThat(email.htmlBody, equalTo(sanitizedHtmlBody));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.email.service;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.elasticsearch.watcher.actions.email.service.EmailTemplate.sanitizeHtml;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
public class HtmlSanitizeTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_onclick() {
|
||||
String badHtml = "<button type=\"button\"" +
|
||||
"onclick=\"document.getElementById('demo').innerHTML = Date()\">" +
|
||||
"Click me to display Date and Time.</button>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("Click me to display Date and Time."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Nonattachment_img() {
|
||||
String badHtml = "<img src=\"http://test.com/nastyimage.jpg\"/>This is a bad image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Goodattachment_img() {
|
||||
String goodHtml = "<img src=\"cid:foo\" />This is a good image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo(goodHtml));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_table() {
|
||||
String goodHtml = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo(goodHtml));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Badattachment_img() {
|
||||
String goodHtml = "<img src=\"cid:bad\" />This is a bad image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Script() {
|
||||
String badHtml = "<script>doSomethingNefarious()</script>This was a dangerous script";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This was a dangerous script"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_FullHtmlWithMetaString() {
|
||||
String needsSanitation = "<html><head></head><body><h1>Hello {{ctx.metadata.name}}</h1> meta <a href='https://www.google.com/search?q={{ctx.metadata.name}}'>Testlink</a>meta</body></html>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(needsSanitation, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("<head></head><body><h1>Hello {{ctx.metadata.name}}</h1> meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a>meta</body>"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HtmlSanitizerTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testDefault_WithTemplatePlaceholders() {
|
||||
String blockTag = randomFrom(HtmlSanitizer.BLOCK_TAGS);
|
||||
while (blockTag.equals("li")) {
|
||||
blockTag = randomFrom(HtmlSanitizer.BLOCK_TAGS);
|
||||
}
|
||||
String html =
|
||||
"<html>" +
|
||||
"<head></head>" +
|
||||
"<body>" +
|
||||
"<" + blockTag + ">Hello {{ctx.metadata.name}}</" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href='https://www.google.com/search?q={{ctx.metadata.name}}'>Testlink</a> meta" +
|
||||
"</body>" +
|
||||
"</html>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
if (blockTag.equals("ol") || blockTag.equals("ul")) {
|
||||
assertThat(sanitizedHtml, equalTo(
|
||||
"<head></head><body>" +
|
||||
"<" + blockTag + "><li>Hello {{ctx.metadata.name}}</li></" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a> meta" +
|
||||
"</body>"));
|
||||
} else {
|
||||
assertThat(sanitizedHtml, equalTo(
|
||||
"<head></head><body>" +
|
||||
"<" + blockTag + ">Hello {{ctx.metadata.name}}</" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a> meta" +
|
||||
"</body>"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDefault_onclick_Disallowed() {
|
||||
String badHtml = "<button type=\"button\"" +
|
||||
"onclick=\"document.getElementById('demo').innerHTML = Date()\">" +
|
||||
"Click me to display Date and Time.</button>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(badHtml);
|
||||
assertThat(sanitizedHtml, equalTo("Click me to display Date and Time."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_ExternalImage_Disallowed() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\"/>This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_EmbeddedImage_Allowed() {
|
||||
String html = "<img src=\"cid:foo\" />This is a good image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_Tables_Allowed() {
|
||||
String html = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_Scipts_Disallowed() {
|
||||
String html = "<script>doSomethingNefarious()</script>This was a dangerous script";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("This was a dangerous script"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_Disabled() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\" />This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.enabled", false)
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_AllImage_Allowed() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\" />This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.allow", "img:all")
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_Tables_Disallowed() {
|
||||
String html = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(Settings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.disallow", "_tables")
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("cell1cell2"));
|
||||
}
|
||||
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.logging;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.throttler;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.test.junit.annotations.TestLogging;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.throttler;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.PeriodType;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
|
|
|
@ -201,9 +201,9 @@ public class WebhookActionTests extends ElasticsearchTestCase {
|
|||
public void testParser_Failure() throws Exception {
|
||||
XContentBuilder builder = jsonBuilder().startObject();
|
||||
if (randomBoolean()) {
|
||||
builder.field(HttpRequestTemplate.Parser.HOST_FIELD.getPreferredName(), TEST_HOST);
|
||||
builder.field(HttpRequest.Field.HOST.getPreferredName(), TEST_HOST);
|
||||
} else {
|
||||
builder.field(HttpRequestTemplate.Parser.PORT_FIELD.getPreferredName(), TEST_PORT);
|
||||
builder.field(HttpRequest.Field.PORT.getPreferredName(), TEST_PORT);
|
||||
}
|
||||
builder.endObject();
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.actions.webhook;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.mockwebserver.QueueDispatcher;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.condition.compare;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.joda.time.DateTime;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.condition.script;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.ShardSearchFailure;
|
||||
|
@ -20,6 +20,7 @@ import org.elasticsearch.search.internal.InternalSearchResponse;
|
|||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.condition.Condition;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.support.Script;
|
||||
import org.elasticsearch.watcher.support.init.proxy.ScriptServiceProxy;
|
||||
|
@ -34,7 +35,9 @@ import java.io.IOException;
|
|||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.getScriptServiceProxy;
|
||||
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
*/
|
||||
|
@ -146,25 +149,28 @@ public class ScriptConditionTests extends ElasticsearchTestCase {
|
|||
fail("expected a condition validation exception trying to create an executable with an invalid language");
|
||||
}
|
||||
|
||||
@Test(expected = ScriptConditionException.class)
|
||||
public void testScriptCondition_throwException() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(new ScriptCondition(Script.inline("assert false").build()), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
condition.execute(ctx);
|
||||
fail("expected a ScriptConditionException trying to execute a script that throws an exception");
|
||||
ScriptCondition.Result result = condition.execute(ctx);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.status(), is(Condition.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), notNullValue());
|
||||
assertThat(result.reason(), containsString("Assertion"));
|
||||
}
|
||||
|
||||
@Test(expected = ScriptConditionException.class)
|
||||
public void testScriptCondition_returnObject() throws Exception {
|
||||
ScriptServiceProxy scriptService = getScriptServiceProxy(tp);
|
||||
ExecutableScriptCondition condition = new ExecutableScriptCondition(new ScriptCondition(Script.inline("return new Object()").build()), logger, scriptService);
|
||||
SearchResponse response = new SearchResponse(InternalSearchResponse.empty(), "", 3, 3, 500l, new ShardSearchFailure[0]);
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", new Payload.XContent(response));
|
||||
condition.execute(ctx);
|
||||
fail();
|
||||
fail("expected a ScriptConditionException trying to execute a script that returns an object");
|
||||
ScriptCondition.Result result = condition.execute(ctx);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.status(), is(Condition.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), notNullValue());
|
||||
assertThat(result.reason(), containsString("ScriptConditionException"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -100,6 +100,7 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
|
||||
// watch level transform
|
||||
Transform.Result watchTransformResult = mock(Transform.Result.class);
|
||||
when(watchTransformResult.status()).thenReturn(Transform.Result.Status.SUCCESS);
|
||||
when(watchTransformResult.payload()).thenReturn(payload);
|
||||
ExecutableTransform watchTransform = mock(ExecutableTransform.class);
|
||||
when(watchTransform.execute(context, payload)).thenReturn(watchTransformResult);
|
||||
|
@ -223,6 +224,216 @@ public class ExecutionServiceTests extends ElasticsearchTestCase {
|
|||
verify(action, never()).execute("_action", context, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_FailedCondition() throws Exception {
|
||||
WatchLockService.Lock lock = mock(WatchLockService.Lock.class);
|
||||
when(watchLockService.acquire("_id")).thenReturn(lock);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", clock.nowUTC(), clock.nowUTC());
|
||||
WatchExecutionContext context = new TriggeredExecutionContext(watch, clock.nowUTC(), event, timeValueSeconds(5));
|
||||
|
||||
ExecutableCondition condition = mock(ExecutableCondition.class);
|
||||
Condition.Result conditionResult = mock(Condition.Result.class);
|
||||
when(conditionResult.status()).thenReturn(Condition.Result.Status.FAILURE);
|
||||
when(conditionResult.reason()).thenReturn("_reason");
|
||||
when(condition.execute(any(WatchExecutionContext.class))).thenReturn(conditionResult);
|
||||
|
||||
// watch level transform
|
||||
Transform.Result watchTransformResult = mock(Transform.Result.class);
|
||||
when(watchTransformResult.payload()).thenReturn(payload);
|
||||
ExecutableTransform watchTransform = mock(ExecutableTransform.class);
|
||||
when(watchTransform.execute(context, payload)).thenReturn(watchTransformResult);
|
||||
|
||||
// action throttler
|
||||
Throttler.Result throttleResult = mock(Throttler.Result.class);
|
||||
when(throttleResult.throttle()).thenReturn(false);
|
||||
ActionThrottler throttler = mock(ActionThrottler.class);
|
||||
when(throttler.throttle("_action", context)).thenReturn(throttleResult);
|
||||
|
||||
// action level transform
|
||||
Transform.Result actionTransformResult = mock(Transform.Result.class);
|
||||
when(actionTransformResult.payload()).thenReturn(payload);
|
||||
ExecutableTransform actionTransform = mock(ExecutableTransform.class);
|
||||
when(actionTransform.execute(context, payload)).thenReturn(actionTransformResult);
|
||||
|
||||
// the action
|
||||
Action.Result actionResult = mock(Action.Result.class);
|
||||
when(actionResult.type()).thenReturn("_action_type");
|
||||
when(actionResult.status()).thenReturn(Action.Result.Status.SUCCESS);
|
||||
ExecutableAction action = mock(ExecutableAction.class);
|
||||
when(action.execute("_action", context, payload)).thenReturn(actionResult);
|
||||
|
||||
ActionWrapper actionWrapper = new ActionWrapper("_action", throttler, actionTransform, action);
|
||||
ExecutableActions actions = new ExecutableActions(Arrays.asList(actionWrapper));
|
||||
|
||||
WatchStatus watchStatus = new WatchStatus(ImmutableMap.of("_action", new ActionStatus(clock.nowUTC())));
|
||||
|
||||
when(watch.input()).thenReturn(input);
|
||||
when(watch.condition()).thenReturn(condition);
|
||||
when(watch.transform()).thenReturn(watchTransform);
|
||||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchRecord watchRecord = executionService.execute(context);
|
||||
assertThat(watchRecord.result().inputResult(), is(inputResult));
|
||||
assertThat(watchRecord.result().conditionResult(), is(conditionResult));
|
||||
assertThat(watchRecord.result().transformResult(), nullValue());
|
||||
assertThat(watchRecord.result().actionsResults(), notNullValue());
|
||||
assertThat(watchRecord.result().actionsResults().count(), is(0));
|
||||
|
||||
verify(historyStore, times(1)).put(watchRecord);
|
||||
verify(lock, times(1)).release();
|
||||
verify(input, times(1)).execute(context);
|
||||
verify(condition, times(1)).execute(context);
|
||||
verify(watchTransform, never()).execute(context, payload);
|
||||
verify(action, never()).execute("_action", context, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_FailedWatchTransform() throws Exception {
|
||||
WatchLockService.Lock lock = mock(WatchLockService.Lock.class);
|
||||
when(watchLockService.acquire("_id")).thenReturn(lock);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", clock.nowUTC(), clock.nowUTC());
|
||||
WatchExecutionContext context = new TriggeredExecutionContext(watch, clock.nowUTC(), event, timeValueSeconds(5));
|
||||
|
||||
Condition.Result conditionResult = AlwaysCondition.Result.INSTANCE;
|
||||
ExecutableCondition condition = mock(ExecutableCondition.class);
|
||||
when(condition.execute(any(WatchExecutionContext.class))).thenReturn(conditionResult);
|
||||
|
||||
// watch level transform
|
||||
Transform.Result watchTransformResult = mock(Transform.Result.class);
|
||||
when(watchTransformResult.status()).thenReturn(Transform.Result.Status.FAILURE);
|
||||
when(watchTransformResult.reason()).thenReturn("_reason");
|
||||
ExecutableTransform watchTransform = mock(ExecutableTransform.class);
|
||||
when(watchTransform.execute(context, payload)).thenReturn(watchTransformResult);
|
||||
|
||||
// action throttler
|
||||
Throttler.Result throttleResult = mock(Throttler.Result.class);
|
||||
when(throttleResult.throttle()).thenReturn(false);
|
||||
ActionThrottler throttler = mock(ActionThrottler.class);
|
||||
when(throttler.throttle("_action", context)).thenReturn(throttleResult);
|
||||
|
||||
// action level transform
|
||||
Transform.Result actionTransformResult = mock(Transform.Result.class);
|
||||
when(actionTransformResult.payload()).thenReturn(payload);
|
||||
ExecutableTransform actionTransform = mock(ExecutableTransform.class);
|
||||
when(actionTransform.execute(context, payload)).thenReturn(actionTransformResult);
|
||||
|
||||
// the action
|
||||
Action.Result actionResult = mock(Action.Result.class);
|
||||
when(actionResult.type()).thenReturn("_action_type");
|
||||
when(actionResult.status()).thenReturn(Action.Result.Status.SUCCESS);
|
||||
ExecutableAction action = mock(ExecutableAction.class);
|
||||
when(action.execute("_action", context, payload)).thenReturn(actionResult);
|
||||
|
||||
ActionWrapper actionWrapper = new ActionWrapper("_action", throttler, actionTransform, action);
|
||||
ExecutableActions actions = new ExecutableActions(Arrays.asList(actionWrapper));
|
||||
|
||||
WatchStatus watchStatus = new WatchStatus(ImmutableMap.of("_action", new ActionStatus(clock.nowUTC())));
|
||||
|
||||
when(watch.input()).thenReturn(input);
|
||||
when(watch.condition()).thenReturn(condition);
|
||||
when(watch.transform()).thenReturn(watchTransform);
|
||||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchRecord watchRecord = executionService.execute(context);
|
||||
assertThat(watchRecord.result().inputResult(), is(inputResult));
|
||||
assertThat(watchRecord.result().conditionResult(), is(conditionResult));
|
||||
assertThat(watchRecord.result().transformResult(), is(watchTransformResult));
|
||||
assertThat(watchRecord.result().actionsResults(), notNullValue());
|
||||
assertThat(watchRecord.result().actionsResults().count(), is(0));
|
||||
|
||||
verify(historyStore, times(1)).put(watchRecord);
|
||||
verify(lock, times(1)).release();
|
||||
verify(input, times(1)).execute(context);
|
||||
verify(condition, times(1)).execute(context);
|
||||
verify(watchTransform, times(1)).execute(context, payload);
|
||||
verify(action, never()).execute("_action", context, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_FailedActionTransform() throws Exception {
|
||||
WatchLockService.Lock lock = mock(WatchLockService.Lock.class);
|
||||
when(watchLockService.acquire("_id")).thenReturn(lock);
|
||||
|
||||
Watch watch = mock(Watch.class);
|
||||
when(watch.id()).thenReturn("_id");
|
||||
when(watchStore.get("_id")).thenReturn(watch);
|
||||
|
||||
ScheduleTriggerEvent event = new ScheduleTriggerEvent("_id", clock.nowUTC(), clock.nowUTC());
|
||||
WatchExecutionContext context = new TriggeredExecutionContext(watch, clock.nowUTC(), event, timeValueSeconds(5));
|
||||
|
||||
Condition.Result conditionResult = AlwaysCondition.Result.INSTANCE;
|
||||
ExecutableCondition condition = mock(ExecutableCondition.class);
|
||||
when(condition.execute(any(WatchExecutionContext.class))).thenReturn(conditionResult);
|
||||
|
||||
// watch level transform
|
||||
Transform.Result watchTransformResult = mock(Transform.Result.class);
|
||||
when(watchTransformResult.status()).thenReturn(Transform.Result.Status.SUCCESS);
|
||||
when(watchTransformResult.payload()).thenReturn(payload);
|
||||
ExecutableTransform watchTransform = mock(ExecutableTransform.class);
|
||||
when(watchTransform.execute(context, payload)).thenReturn(watchTransformResult);
|
||||
|
||||
// action throttler
|
||||
Throttler.Result throttleResult = mock(Throttler.Result.class);
|
||||
when(throttleResult.throttle()).thenReturn(false);
|
||||
ActionThrottler throttler = mock(ActionThrottler.class);
|
||||
when(throttler.throttle("_action", context)).thenReturn(throttleResult);
|
||||
|
||||
// action level transform
|
||||
Transform.Result actionTransformResult = mock(Transform.Result.class);
|
||||
when(actionTransformResult.status()).thenReturn(Transform.Result.Status.FAILURE);
|
||||
when(actionTransformResult.reason()).thenReturn("_reason");
|
||||
ExecutableTransform actionTransform = mock(ExecutableTransform.class);
|
||||
when(actionTransform.execute(context, payload)).thenReturn(actionTransformResult);
|
||||
|
||||
// the action
|
||||
Action.Result actionResult = mock(Action.Result.class);
|
||||
when(actionResult.type()).thenReturn("_action_type");
|
||||
when(actionResult.status()).thenReturn(Action.Result.Status.SUCCESS);
|
||||
ExecutableAction action = mock(ExecutableAction.class);
|
||||
when(action.logger()).thenReturn(logger);
|
||||
when(action.execute("_action", context, payload)).thenReturn(actionResult);
|
||||
|
||||
ActionWrapper actionWrapper = new ActionWrapper("_action", throttler, actionTransform, action);
|
||||
ExecutableActions actions = new ExecutableActions(Arrays.asList(actionWrapper));
|
||||
|
||||
WatchStatus watchStatus = new WatchStatus(ImmutableMap.of("_action", new ActionStatus(clock.nowUTC())));
|
||||
|
||||
when(watch.input()).thenReturn(input);
|
||||
when(watch.condition()).thenReturn(condition);
|
||||
when(watch.transform()).thenReturn(watchTransform);
|
||||
when(watch.actions()).thenReturn(actions);
|
||||
when(watch.status()).thenReturn(watchStatus);
|
||||
|
||||
WatchRecord watchRecord = executionService.execute(context);
|
||||
assertThat(watchRecord.result().inputResult(), is(inputResult));
|
||||
assertThat(watchRecord.result().conditionResult(), is(conditionResult));
|
||||
assertThat(watchRecord.result().transformResult(), is(watchTransformResult));
|
||||
assertThat(watchRecord.result().actionsResults(), notNullValue());
|
||||
assertThat(watchRecord.result().actionsResults().count(), is(1));
|
||||
assertThat(watchRecord.result().actionsResults().get("_action").transform(), is(actionTransformResult));
|
||||
assertThat(watchRecord.result().actionsResults().get("_action").action().status(), is(Action.Result.Status.FAILURE));
|
||||
|
||||
verify(historyStore, times(1)).put(watchRecord);
|
||||
verify(lock, times(1)).release();
|
||||
verify(input, times(1)).execute(context);
|
||||
verify(condition, times(1)).execute(context);
|
||||
verify(watchTransform, times(1)).execute(context, payload);
|
||||
// the action level transform is executed before the action itself
|
||||
verify(action, never()).execute("_action", context, payload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteInner() throws Exception {
|
||||
DateTime now = DateTime.now(DateTimeZone.UTC);
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.execution;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.apache.lucene.util.LuceneTestCase.Slow;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
|
@ -24,6 +23,9 @@ import org.elasticsearch.watcher.support.Script;
|
|||
import org.elasticsearch.watcher.support.xcontent.ObjectPath;
|
||||
import org.elasticsearch.watcher.test.AbstractWatcherIntegrationTests;
|
||||
import org.elasticsearch.watcher.transport.actions.delete.DeleteWatchResponse;
|
||||
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchRequest;
|
||||
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchRequestBuilder;
|
||||
import org.elasticsearch.watcher.transport.actions.execute.ExecuteWatchResponse;
|
||||
import org.elasticsearch.watcher.transport.actions.get.GetWatchRequest;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchRequest;
|
||||
import org.elasticsearch.watcher.transport.actions.put.PutWatchResponse;
|
||||
|
@ -62,7 +64,6 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
|
||||
@Test
|
||||
public void testExecuteWatch() throws Exception {
|
||||
ensureWatcherStarted();
|
||||
boolean ignoreCondition = randomBoolean();
|
||||
boolean recordExecution = randomBoolean();
|
||||
boolean conditionAlwaysTrue = randomBoolean();
|
||||
|
@ -146,9 +147,73 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutionWithInlineWatch() throws Exception {
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(alwaysCondition())
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
ExecuteWatchRequestBuilder builder = watcherClient().prepareExecuteWatch()
|
||||
.setWatchSource(watchBuilder);
|
||||
if (randomBoolean()) {
|
||||
builder.setRecordExecution(false);
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
builder.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)));
|
||||
}
|
||||
|
||||
ExecuteWatchResponse executeWatchResponse = builder.get();
|
||||
assertThat(executeWatchResponse.getRecordId(), startsWith(ExecuteWatchRequest.INLINE_WATCH_ID));
|
||||
assertThat(executeWatchResponse.getRecordSource().getValue("watch_id").toString(), equalTo(ExecuteWatchRequest.INLINE_WATCH_ID));
|
||||
assertThat(executeWatchResponse.getRecordSource().getValue("state").toString(), equalTo("executed"));
|
||||
assertThat(executeWatchResponse.getRecordSource().getValue("trigger_event.type").toString(), equalTo("manual"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutionWithInlineWatch_withRecordExecutionEnabled() throws Exception {
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(alwaysCondition())
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
try {
|
||||
watcherClient().prepareExecuteWatch()
|
||||
.setWatchSource(watchBuilder)
|
||||
.setRecordExecution(true)
|
||||
.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)))
|
||||
.get();
|
||||
fail();
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("the execution of an inline watch cannot be recorded"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecutionWithInlineWatch_withWatchId() throws Exception {
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
.condition(alwaysCondition())
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
||||
try {
|
||||
watcherClient().prepareExecuteWatch()
|
||||
.setId("_id")
|
||||
.setWatchSource(watchBuilder)
|
||||
.setRecordExecution(false)
|
||||
.setTriggerEvent(new ScheduleTriggerEvent(new DateTime(DateTimeZone.UTC), new DateTime(DateTimeZone.UTC)))
|
||||
.get();
|
||||
fail();
|
||||
} catch (ActionRequestValidationException e) {
|
||||
assertThat(e.getMessage(), containsString("a watch execution request must either have a watch id or an inline watch source but not both"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDifferentAlternativeInputs() throws Exception {
|
||||
ensureWatcherStarted();
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.addAction("log", loggingAction("foobar"));
|
||||
|
@ -187,8 +252,6 @@ public class ManualExecutionTests extends AbstractWatcherIntegrationTests {
|
|||
|
||||
@Test
|
||||
public void testExecutionRequestDefaults() throws Exception {
|
||||
ensureWatcherStarted();
|
||||
|
||||
WatchSourceBuilder watchBuilder = watchBuilder()
|
||||
.trigger(schedule(cron("0 0 0 1 * ? 2099")))
|
||||
.input(simpleInput("foo", "bar"))
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.input.http;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
|
|
|
@ -41,7 +41,7 @@ import static org.hamcrest.core.Is.is;
|
|||
|
||||
/**
|
||||
*/
|
||||
public class HttpClientTest extends ElasticsearchTestCase {
|
||||
public class HttpClientTests extends ElasticsearchTestCase {
|
||||
|
||||
private MockWebServer webServer;
|
||||
private HttpClient httpClient;
|
||||
|
@ -163,7 +163,7 @@ public class HttpClientTest extends ElasticsearchTestCase {
|
|||
|
||||
// We can't use the client created above for the server since it is only a truststore
|
||||
webServer.useHttps(new HttpClient(Settings.builder()
|
||||
.put(HttpClient.SETTINGS_SSL_KEYSTORE, PathUtils.get(HttpClientTest.class.getResource("/org/elasticsearch/shield/keystore/testnode.jks").toURI()))
|
||||
.put(HttpClient.SETTINGS_SSL_KEYSTORE, getDataPath("/org/elasticsearch/shield/keystore/testnode.jks"))
|
||||
.put(HttpClient.SETTINGS_SSL_KEYSTORE_PASSWORD, "testnode")
|
||||
.build(), authRegistry, environment)
|
||||
.start()
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.support.http;
|
||||
|
||||
import org.elasticsearch.ElasticsearchTimeoutException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class HttpConnectionTimeoutTests extends ElasticsearchTestCase {
|
||||
|
||||
// setting an unroutable IP to simulate a connection timeout
|
||||
private static final String UNROUTABLE_IP = "192.168.255.255";
|
||||
|
||||
@Test @Slow
|
||||
public void testDefaultTimeout() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
HttpClient httpClient = new HttpClient(Settings.EMPTY, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
HttpRequest request = HttpRequest.builder(UNROUTABLE_IP, 12345)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected timeout exception");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
// it's supposed to be 10, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(8L));
|
||||
assertThat(timeout.seconds(), lessThan(12L));
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test @Slow
|
||||
public void testDefaultTimeout_Custom() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
HttpClient httpClient = new HttpClient(Settings.builder()
|
||||
.put("watcher.http.default_connection_timeout", "5s")
|
||||
.build()
|
||||
, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
HttpRequest request = HttpRequest.builder(UNROUTABLE_IP, 12345)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected timeout exception");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
// it's supposed to be 7, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(3L));
|
||||
assertThat(timeout.seconds(), lessThan(7L));
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test @Slow
|
||||
public void testTimeout_CustomPerRequest() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
HttpClient httpClient = new HttpClient(Settings.builder()
|
||||
.put("watcher.http.default_connection_timeout", "10s")
|
||||
.build()
|
||||
, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
HttpRequest request = HttpRequest.builder(UNROUTABLE_IP, 12345)
|
||||
.connectionTimeout(TimeValue.timeValueSeconds(5))
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected timeout exception");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
// it's supposed to be 7, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(3L));
|
||||
assertThat(timeout.seconds(), lessThan(7L));
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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.support.http;
|
||||
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchTimeoutException;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.watcher.support.http.auth.HttpAuthRegistry;
|
||||
import org.elasticsearch.watcher.support.secret.SecretService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.BindException;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class HttpReadTimeoutTests extends ElasticsearchTestCase {
|
||||
|
||||
private MockWebServer webServer;
|
||||
private SecretService secretService;
|
||||
private int webPort;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
secretService = new SecretService.PlainText();
|
||||
for (webPort = 9200; webPort < 9300; webPort++) {
|
||||
try {
|
||||
webServer = new MockWebServer();
|
||||
webServer.start(webPort);
|
||||
return;
|
||||
} catch (BindException be) {
|
||||
logger.warn("port [{}] was already in use trying next port", webPort);
|
||||
}
|
||||
}
|
||||
throw new ElasticsearchException("unable to find open port between 9200 and 9300");
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
webServer.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTimeout() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
HttpClient httpClient = new HttpClient(Settings.EMPTY, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
// we're not going to enqueue an response... so the server will just hang
|
||||
|
||||
HttpRequest request = HttpRequest.builder("localhost", webPort)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected read timeout after 10 seconds (default)");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
// expected
|
||||
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
|
||||
// it's supposed to be 10, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(8L));
|
||||
assertThat(timeout.seconds(), lessThan(12L));
|
||||
|
||||
// lets enqueue a response to relese the server.
|
||||
webServer.enqueue(new MockResponse());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTimeout_Custom() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
|
||||
HttpClient httpClient = new HttpClient(Settings.builder()
|
||||
.put("watcher.http.default_read_timeout", "5s")
|
||||
.build()
|
||||
, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
// we're not going to enqueue an response... so the server will just hang
|
||||
|
||||
HttpRequest request = HttpRequest.builder("localhost", webPort)
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected read timeout after 5 seconds (default)");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
// expected
|
||||
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
|
||||
// it's supposed to be 5, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(3L));
|
||||
assertThat(timeout.seconds(), lessThan(7L));
|
||||
|
||||
// lets enqueue a response to relese the server.
|
||||
webServer.enqueue(new MockResponse());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout_CustomPerRequest() throws Exception {
|
||||
Environment environment = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
||||
|
||||
HttpClient httpClient = new HttpClient(Settings.builder()
|
||||
.put("watcher.http.default_read_timeout", "10s")
|
||||
.build()
|
||||
, mock(HttpAuthRegistry.class), environment).start();
|
||||
|
||||
// we're not going to enqueue an response... so the server will just hang
|
||||
|
||||
HttpRequest request = HttpRequest.builder("localhost", webPort)
|
||||
.readTimeout(TimeValue.timeValueSeconds(5))
|
||||
.method(HttpMethod.POST)
|
||||
.path("/" + randomAsciiOfLength(5))
|
||||
.build();
|
||||
|
||||
long start = System.nanoTime();
|
||||
try {
|
||||
httpClient.execute(request);
|
||||
fail("expected read timeout after 5 seconds (default)");
|
||||
} catch (ElasticsearchTimeoutException ete) {
|
||||
// expected
|
||||
|
||||
TimeValue timeout = TimeValue.timeValueNanos(System.nanoTime() - start);
|
||||
logger.info("http connection timed out after {}", timeout.format());
|
||||
|
||||
// it's supposed to be 5, but we'll give it an error margin of 2 seconds
|
||||
assertThat(timeout.seconds(), greaterThan(3L));
|
||||
assertThat(timeout.seconds(), lessThan(7L));
|
||||
|
||||
// lets enqueue a response to relese the server.
|
||||
webServer.enqueue(new MockResponse());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.http;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -79,6 +79,14 @@ public class HttpRequestTemplateTests extends ElasticsearchTestCase {
|
|||
if (randomBoolean()) {
|
||||
builder.putHeader("_key", Template.inline("_value"));
|
||||
}
|
||||
long connectionTimeout = randomBoolean() ? 0 : randomIntBetween(5, 10);
|
||||
if (connectionTimeout > 0) {
|
||||
builder.connectionTimeout(TimeValue.timeValueSeconds(connectionTimeout));
|
||||
}
|
||||
long readTimeout = randomBoolean() ? 0 : randomIntBetween(5, 10);
|
||||
if (readTimeout > 0) {
|
||||
builder.readTimeout(TimeValue.timeValueSeconds(readTimeout));
|
||||
}
|
||||
|
||||
HttpRequestTemplate template = builder.build();
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.http;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.template;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
|
@ -86,7 +86,7 @@ public class TemplateTests extends ElasticsearchTestCase {
|
|||
assertThat(engine.render(template, model), is("rendered_text"));
|
||||
}
|
||||
|
||||
@Test @Repeat(iterations = 5)
|
||||
@Test
|
||||
public void testParser() throws Exception {
|
||||
ScriptType type = randomScriptType();
|
||||
Template template = templateBuilder(type, "_template").params(ImmutableMap.<String, Object>of("param_key", "param_val")).build();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.template.xmustache;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
package org.elasticsearch.watcher.support.template.xmustache;
|
||||
|
||||
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableList;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.xcontent;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.support.xcontent;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
|
|
@ -11,7 +11,6 @@ import org.elasticsearch.action.support.IndicesOptions;
|
|||
import org.elasticsearch.common.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.script.ScriptContext;
|
||||
import org.elasticsearch.script.ScriptContextRegistry;
|
||||
import org.joda.time.DateTime;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
|
@ -22,7 +21,6 @@ import org.elasticsearch.common.xcontent.XContentHelper;
|
|||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.script.ScriptEngineService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.script.groovy.GroovyScriptEngineService;
|
||||
|
@ -35,10 +33,7 @@ import org.elasticsearch.watcher.actions.ActionWrapper;
|
|||
import org.elasticsearch.watcher.actions.ExecutableActions;
|
||||
import org.elasticsearch.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.ExecutableEmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.service.Authentication;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.actions.email.service.*;
|
||||
import org.elasticsearch.watcher.actions.webhook.ExecutableWebhookAction;
|
||||
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
|
||||
import org.elasticsearch.watcher.condition.script.ExecutableScriptCondition;
|
||||
|
@ -200,7 +195,7 @@ public final class WatcherTestUtils {
|
|||
Authentication auth = new Authentication("testname", new Secret("testpassword".toCharArray()));
|
||||
|
||||
EmailAction action = new EmailAction(email, "testaccount", auth, Profile.STANDARD, null);
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine);
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine, new HtmlSanitizer(Settings.EMPTY));
|
||||
|
||||
actions.add(new ActionWrapper("_email", executale));
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock;
|
|||
public class ChainTransformTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void testApply() throws Exception {
|
||||
public void testExecute() throws Exception {
|
||||
ChainTransform transform = new ChainTransform(ImmutableList.<Transform>of(
|
||||
new NamedExecutableTransform.Transform("name1"),
|
||||
new NamedExecutableTransform.Transform("name2"),
|
||||
|
@ -51,7 +51,21 @@ public class ChainTransformTests extends ElasticsearchTestCase {
|
|||
WatchExecutionContext ctx = mock(WatchExecutionContext.class);
|
||||
Payload payload = new Payload.Simple(new HashMap<String, Object>());
|
||||
|
||||
Transform.Result result = executable.execute(ctx, payload);
|
||||
ChainTransform.Result result = executable.execute(ctx, payload);
|
||||
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat(result.results(), hasSize(3));
|
||||
assertThat(result.results().get(0), instanceOf(NamedExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(0).status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat((List<String>) result.results().get(0).payload().data().get("names"), hasSize(1));
|
||||
assertThat((List<String>) result.results().get(0).payload().data().get("names"), contains("name1"));
|
||||
assertThat(result.results().get(1), instanceOf(NamedExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(1).status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat((List<String>) result.results().get(1).payload().data().get("names"), hasSize(2));
|
||||
assertThat((List<String>) result.results().get(1).payload().data().get("names"), contains("name1", "name2"));
|
||||
assertThat(result.results().get(2), instanceOf(NamedExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(2).status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat((List<String>) result.results().get(2).payload().data().get("names"), hasSize(3));
|
||||
assertThat((List<String>) result.results().get(2).payload().data().get("names"), contains("name1", "name2", "name3"));
|
||||
|
||||
Map<String, Object> data = result.payload().data();
|
||||
assertThat(data, notNullValue());
|
||||
|
@ -62,6 +76,39 @@ public class ChainTransformTests extends ElasticsearchTestCase {
|
|||
assertThat(names, contains("name1", "name2", "name3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_Failure() throws Exception {
|
||||
ChainTransform transform = new ChainTransform(ImmutableList.of(
|
||||
new NamedExecutableTransform.Transform("name1"),
|
||||
new NamedExecutableTransform.Transform("name2"),
|
||||
new FailingExecutableTransform.Transform()
|
||||
));
|
||||
ExecutableChainTransform executable = new ExecutableChainTransform(transform, logger, ImmutableList.<ExecutableTransform>of(
|
||||
new NamedExecutableTransform("name1"),
|
||||
new NamedExecutableTransform("name2"),
|
||||
new FailingExecutableTransform(logger)));
|
||||
|
||||
WatchExecutionContext ctx = mock(WatchExecutionContext.class);
|
||||
Payload payload = new Payload.Simple(new HashMap<String, Object>());
|
||||
|
||||
ChainTransform.Result result = executable.execute(ctx, payload);
|
||||
assertThat(result.status(), is(Transform.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), notNullValue());
|
||||
assertThat(result.results(), hasSize(3));
|
||||
assertThat(result.results().get(0), instanceOf(NamedExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(0).status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat((List<String>) result.results().get(0).payload().data().get("names"), hasSize(1));
|
||||
assertThat((List<String>) result.results().get(0).payload().data().get("names"), contains("name1"));
|
||||
assertThat(result.results().get(1), instanceOf(NamedExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(1).status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat((List<String>) result.results().get(1).payload().data().get("names"), hasSize(2));
|
||||
assertThat((List<String>) result.results().get(1).payload().data().get("names"), contains("name1", "name2"));
|
||||
assertThat(result.results().get(2), instanceOf(FailingExecutableTransform.Result.class));
|
||||
assertThat(result.results().get(2).status(), is(Transform.Result.Status.FAILURE));
|
||||
assertThat(result.results().get(2).reason(), containsString("_error"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParser() throws Exception {
|
||||
Map<String, TransformFactory> factories = ImmutableMap.<String, TransformFactory>builder()
|
||||
|
@ -103,14 +150,16 @@ public class ChainTransformTests extends ElasticsearchTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result execute(WatchExecutionContext ctx, Payload payload) throws IOException {
|
||||
Map<String, Object> data = new HashMap<>(payload.data());
|
||||
List<String> names = (List<String>) data.get("names");
|
||||
public Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
List<String> names = (List<String>) payload.data().get("names");
|
||||
if (names == null) {
|
||||
names = new ArrayList<>();
|
||||
data.put("names", names);
|
||||
} else {
|
||||
names = new ArrayList<>(names);
|
||||
}
|
||||
names.add(transform.name);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("names", names);
|
||||
return new Result("named", new Payload.Simple(data));
|
||||
}
|
||||
|
||||
|
@ -178,4 +227,68 @@ public class ChainTransformTests extends ElasticsearchTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingExecutableTransform extends ExecutableTransform<FailingExecutableTransform.Transform, FailingExecutableTransform.Result> {
|
||||
|
||||
private static final String TYPE = "throwing";
|
||||
|
||||
public FailingExecutableTransform(ESLogger logger) {
|
||||
super(new Transform(), logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result execute(WatchExecutionContext ctx, Payload payload) {
|
||||
return new Result(TYPE);
|
||||
}
|
||||
|
||||
public static class Transform implements org.elasticsearch.watcher.transform.Transform {
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder.startObject().endArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Result extends Transform.Result {
|
||||
|
||||
public Result(String type) {
|
||||
super(type, new Exception("_error"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected XContentBuilder typeXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory extends TransformFactory<Transform, Result, FailingExecutableTransform> {
|
||||
|
||||
public Factory(ESLogger transformLogger) {
|
||||
super(transformLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transform parseTransform(String watchId, XContentParser parser) throws IOException {
|
||||
assert parser.currentToken() == XContentParser.Token.START_OBJECT;
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.END_OBJECT;
|
||||
return new Transform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FailingExecutableTransform createExecutable(Transform transform) {
|
||||
return new FailingExecutableTransform(transformLogger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ package org.elasticsearch.watcher.transform.script;
|
|||
|
||||
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableList;
|
||||
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableSet;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -58,7 +58,7 @@ public class ScriptTransformTests extends ElasticsearchTestCase {
|
|||
|
||||
|
||||
@Test
|
||||
public void testApply_MapValue() throws Exception {
|
||||
public void testExecute_MapValue() throws Exception {
|
||||
ScriptServiceProxy service = mock(ScriptServiceProxy.class);
|
||||
ScriptType type = randomFrom(ScriptType.values());
|
||||
Map<String, Object> params = Collections.emptyMap();
|
||||
|
@ -84,11 +84,39 @@ public class ScriptTransformTests extends ElasticsearchTestCase {
|
|||
Transform.Result result = transform.execute(ctx, payload);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.type(), is(ScriptTransform.TYPE));
|
||||
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
|
||||
assertThat(result.payload().data(), equalTo(transformed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApply_NonMapValue() throws Exception {
|
||||
public void testExecute_MapValue_Failure() throws Exception {
|
||||
ScriptServiceProxy service = mock(ScriptServiceProxy.class);
|
||||
ScriptType type = randomFrom(ScriptType.values());
|
||||
Map<String, Object> params = Collections.emptyMap();
|
||||
Script script = scriptBuilder(type, "_script").lang("_lang").params(params).build();
|
||||
CompiledScript compiledScript = mock(CompiledScript.class);
|
||||
when(service.compile(script)).thenReturn(compiledScript);
|
||||
ExecutableScriptTransform transform = new ExecutableScriptTransform(new ScriptTransform(script), logger, service);
|
||||
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
|
||||
|
||||
Payload payload = simplePayload("key", "value");
|
||||
|
||||
Map<String, Object> model = Variables.createCtxModel(ctx, payload);
|
||||
|
||||
ExecutableScript executable = mock(ExecutableScript.class);
|
||||
when(executable.run()).thenThrow(new RuntimeException("_error"));
|
||||
when(service.executable(compiledScript, model)).thenReturn(executable);
|
||||
|
||||
Transform.Result result = transform.execute(ctx, payload);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.type(), is(ScriptTransform.TYPE));
|
||||
assertThat(result.status(), is(Transform.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), containsString("_error"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_NonMapValue() throws Exception {
|
||||
ScriptServiceProxy service = mock(ScriptServiceProxy.class);
|
||||
|
||||
ScriptType type = randomFrom(ScriptType.values());
|
||||
|
|
|
@ -114,7 +114,7 @@ public class SearchTransformTests extends ElasticsearchIntegrationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testApply() throws Exception {
|
||||
public void testExecute() throws Exception {
|
||||
|
||||
index("idx", "type", "1");
|
||||
ensureGreen("idx");
|
||||
|
@ -133,6 +133,7 @@ public class SearchTransformTests extends ElasticsearchIntegrationTest {
|
|||
Transform.Result result = transform.execute(ctx, EMPTY_PAYLOAD);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.type(), is(SearchTransform.TYPE));
|
||||
assertThat(result.status(), is(Transform.Result.Status.SUCCESS));
|
||||
|
||||
SearchResponse response = client().search(request).get();
|
||||
Payload expectedPayload = new Payload.XContent(response);
|
||||
|
@ -149,7 +150,33 @@ public class SearchTransformTests extends ElasticsearchIntegrationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testApply_MustacheTemplate() throws Exception {
|
||||
public void testExecute_Failure() throws Exception {
|
||||
|
||||
index("idx", "type", "1");
|
||||
ensureGreen("idx");
|
||||
refresh();
|
||||
|
||||
// create a bad request
|
||||
SearchRequest request = Requests.searchRequest("idx").source(jsonBuilder().startObject()
|
||||
.startObject("query")
|
||||
.startObject("_unknown_query_").endObject()
|
||||
.endObject()
|
||||
.endObject());
|
||||
SearchTransform searchTransform = TransformBuilders.searchTransform(request).build();
|
||||
ExecutableSearchTransform transform = new ExecutableSearchTransform(searchTransform, logger, ClientProxy.of(client()));
|
||||
|
||||
WatchExecutionContext ctx = mockExecutionContext("_name", EMPTY_PAYLOAD);
|
||||
|
||||
SearchTransform.Result result = transform.execute(ctx, EMPTY_PAYLOAD);
|
||||
assertThat(result, notNullValue());
|
||||
assertThat(result.type(), is(SearchTransform.TYPE));
|
||||
assertThat(result.status(), is(Transform.Result.Status.FAILURE));
|
||||
assertThat(result.reason(), notNullValue());
|
||||
assertThat(result.executedRequest().templateSource().toUtf8(), containsString("_unknown_query_"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_MustacheTemplate() throws Exception {
|
||||
|
||||
// The rational behind this test:
|
||||
//
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.transport.action.ack;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.transport.action.execute;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.joda.time.DateTime;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.transport.action.put;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.client.WatchSourceBuilder;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.watcher.trigger.schedule.tool;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.cli.CliTool;
|
||||
import org.elasticsearch.common.cli.CliToolTestCase;
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.elasticsearch.watcher.actions.email.EmailActionFactory;
|
|||
import org.elasticsearch.watcher.actions.email.ExecutableEmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.actions.index.ExecutableIndexAction;
|
||||
import org.elasticsearch.watcher.actions.index.IndexAction;
|
||||
|
@ -113,6 +114,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
private HttpClient httpClient;
|
||||
private EmailService emailService;
|
||||
private TemplateEngine templateEngine;
|
||||
private HtmlSanitizer htmlSanitizer;
|
||||
private HttpAuthRegistry authRegistry;
|
||||
private SecretService secretService;
|
||||
private LicenseService licenseService;
|
||||
|
@ -126,6 +128,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
httpClient = mock(HttpClient.class);
|
||||
emailService = mock(EmailService.class);
|
||||
templateEngine = mock(TemplateEngine.class);
|
||||
htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
secretService = mock(SecretService.class);
|
||||
licenseService = mock(LicenseService.class);
|
||||
authRegistry = new HttpAuthRegistry(ImmutableMap.of("basic", (HttpAuthFactory) new BasicAuthFactory(secretService)));
|
||||
|
@ -383,7 +386,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
if (randomBoolean()) {
|
||||
ExecutableTransform transform = randomTransform();
|
||||
EmailAction action = new EmailAction(EmailTemplate.builder().build(), null, null, Profile.STANDARD, randomFrom(DataAttachment.JSON, DataAttachment.YAML, null));
|
||||
list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine)));
|
||||
list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine, htmlSanitizer)));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
IndexAction aciton = new IndexAction("_index", "_type", null);
|
||||
|
@ -405,7 +408,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
for (ActionWrapper action : actions) {
|
||||
switch (action.action().type()) {
|
||||
case EmailAction.TYPE:
|
||||
parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine));
|
||||
parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine, htmlSanitizer));
|
||||
break;
|
||||
case IndexAction.TYPE:
|
||||
parsers.put(IndexAction.TYPE, new IndexActionFactory(settings, client));
|
||||
|
|
Loading…
Reference in New Issue