Added the watch metadata to the template/script model

The watch metadata is now accessible to the templates and scripts (under `ctx.metadata`).

Also, changed the default email attachment to include the ctx model as a whole (not just the payload). This provides more context to the watch exectuion (e.g. watch id, execution time, metadata, etc...)

Original commit: elastic/x-pack-elasticsearch@c5dde855d2
This commit is contained in:
uboness 2015-04-20 20:56:56 +02:00
parent ca38fd6a89
commit 02ba76fe21
10 changed files with 206 additions and 67 deletions

View File

@ -29,14 +29,14 @@ public class EmailAction implements Action {
private final @Nullable String account;
private final @Nullable Authentication auth;
private final @Nullable Profile profile;
private final @Nullable Boolean attachPayload;
private final @Nullable Boolean attachData;
public EmailAction(EmailTemplate email, @Nullable String account, @Nullable Authentication auth, @Nullable Profile profile, @Nullable Boolean attachPayload) {
public EmailAction(EmailTemplate email, @Nullable String account, @Nullable Authentication auth, @Nullable Profile profile, @Nullable Boolean attachData) {
this.email = email;
this.account = account;
this.auth = auth;
this.profile = profile;
this.attachPayload = attachPayload;
this.attachData = attachData;
}
public EmailTemplate getEmail() {
@ -55,8 +55,8 @@ public class EmailAction implements Action {
return profile;
}
public boolean isAttachPayload() {
return attachPayload != null && attachPayload;
public boolean getAttachData() {
return attachData != null && attachData;
}
@Override
@ -75,7 +75,7 @@ public class EmailAction implements Action {
if (account != null ? !account.equals(action.account) : action.account != null) return false;
if (auth != null ? !auth.equals(action.auth) : action.auth != null) return false;
if (profile != action.profile) return false;
return !(attachPayload != null ? !attachPayload.equals(action.attachPayload) : action.attachPayload != null);
return !(attachData != null ? !attachData.equals(action.attachData) : action.attachData != null);
}
@Override
@ -84,7 +84,7 @@ public class EmailAction implements Action {
result = 31 * result + (account != null ? account.hashCode() : 0);
result = 31 * result + (auth != null ? auth.hashCode() : 0);
result = 31 * result + (profile != null ? profile.hashCode() : 0);
result = 31 * result + (attachPayload != null ? attachPayload.hashCode() : 0);
result = 31 * result + (attachData != null ? attachData.hashCode() : 0);
return result;
}
@ -101,8 +101,8 @@ public class EmailAction implements Action {
if (profile != null) {
builder.field(Field.PROFILE.getPreferredName(), profile.name().toLowerCase(Locale.ROOT));
}
if (attachPayload != null) {
builder.field(Field.ATTACH_PAYLOAD.getPreferredName(), attachPayload);
if (attachData != null) {
builder.field(Field.ATTACH_DATA.getPreferredName(), attachData);
}
email.xContentBody(builder, params);
return builder.endObject();
@ -135,7 +135,7 @@ public class EmailAction implements Action {
throw new EmailActionException("could not parse [{}] action [{}/{}]. unexpected string field [{}]", TYPE, watchId, actionId, currentFieldName);
}
} else if (token == XContentParser.Token.VALUE_BOOLEAN) {
if (Field.ATTACH_PAYLOAD.match(currentFieldName)) {
if (Field.ATTACH_DATA.match(currentFieldName)) {
attachPayload = parser.booleanValue();
} else {
throw new EmailActionException("could not parse [{}] action [{}/{}]. unexpected boolean field [{}]", TYPE, watchId, actionId, currentFieldName);
@ -342,7 +342,7 @@ public class EmailAction implements Action {
ParseField PROFILE = new ParseField("profile");
ParseField USER = new ParseField("user");
ParseField PASSWORD = new ParseField("password");
ParseField ATTACH_PAYLOAD = new ParseField("attach_payload");
ParseField ATTACH_DATA = new ParseField("attach_data");
// result fields
ParseField EMAIL = new ParseField("email");

View File

@ -36,8 +36,8 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction, EmailAc
Email.Builder email = action.getEmail().render(templateEngine, model);
email.id(ctx.id().value());
if (action.isAttachPayload()) {
Attachment.Bytes attachment = new Attachment.XContent.Yaml("payload", "payload.yml", payload);
if (action.getAttachData()) {
Attachment.Bytes attachment = new Attachment.XContent.Yaml("data", "data.yml", new Payload.Simple(model));
email.attach(attachment);
}

View File

@ -6,10 +6,9 @@
package org.elasticsearch.watcher.execution;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.watcher.actions.ExecutableActions;
import org.elasticsearch.watcher.actions.ActionWrapper;
import org.elasticsearch.watcher.actions.ExecutableActions;
import org.elasticsearch.watcher.condition.Condition;
import org.elasticsearch.watcher.condition.ExecutableCondition;
import org.elasticsearch.watcher.input.Input;
import org.elasticsearch.watcher.throttle.Throttler;
import org.elasticsearch.watcher.transform.Transform;

View File

@ -5,10 +5,8 @@
*/
package org.elasticsearch.watcher.support;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.common.joda.time.DateTime;
import java.util.HashMap;
import java.util.Map;
@ -23,22 +21,21 @@ public final class Variables {
public static final String EXECUTION_TIME = "execution_time";
public static final String TRIGGER = "trigger";
public static final String PAYLOAD = "payload";
public static final String METADATA = "metadata";
public static Map<String, Object> createCtxModel(WatchExecutionContext ctx, Payload payload) {
return createCtxModel(ctx.watch().name(), ctx.executionTime(), ctx.triggerEvent(), payload);
}
public static Map<String, Object> createCtxModel(String watchName, DateTime executionTime, TriggerEvent triggerEvent, Payload payload) {
Map<String, Object> vars = new HashMap<>();
vars.put(WATCH_ID, watchName);
vars.put(EXECUTION_TIME, executionTime);
vars.put(TRIGGER, triggerEvent.data());
vars.put(WATCH_ID, ctx.watch().name());
vars.put(EXECUTION_TIME, ctx.executionTime());
vars.put(TRIGGER, ctx.triggerEvent().data());
if (payload != null) {
vars.put(PAYLOAD, payload.data());
}
vars.put(METADATA, ctx.watch().metadata());
Map<String, Object> model = new HashMap<>();
model.put(CTX, vars);
return model;
}
}

View File

@ -8,6 +8,7 @@ package org.elasticsearch.watcher.actions.email;
import com.carrotsearch.randomizedtesting.annotations.Repeat;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.xcontent.ToXContent;
@ -24,13 +25,12 @@ import org.elasticsearch.watcher.support.template.TemplateEngine;
import org.elasticsearch.watcher.watch.Payload;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContext;
import static org.elasticsearch.watcher.test.WatcherTestUtils.mockExecutionContextBuilder;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -82,28 +82,26 @@ public class EmailActionTests extends ElasticsearchTestCase {
EmailAction action = new EmailAction(email, account, auth, profile, attachPayload);
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine);
final Map<String, Object> data = new HashMap<>();
Payload payload = new Payload() {
@Override
public Map<String, Object> data() {
return data;
}
Map<String, Object> data = new HashMap<>();
Payload payload = new Payload.Simple(data);
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder.map(data);
}
};
Map<String, Object> metadata = MapBuilder.<String, Object>newMapBuilder().put("_key", "_val").map();
DateTime now = DateTime.now(UTC);
Wid wid = new Wid(randomAsciiOfLength(5), randomLong(), DateTime.now(UTC));
WatchExecutionContext ctx = mockExecutionContext("watch1", now, payload);
when(ctx.id()).thenReturn(wid);
Wid wid = new Wid(randomAsciiOfLength(5), randomLong(), now);
WatchExecutionContext ctx = mockExecutionContextBuilder("watch1")
.wid(wid)
.payload(payload)
.time(now)
.metadata(metadata)
.buildMock();
Map<String, Object> expectedModel = ImmutableMap.<String, Object>builder()
.put("ctx", ImmutableMap.<String, Object>builder()
.put("watch_id", "watch1")
.put("payload", data)
.put("metadata", metadata)
.put("execution_time", now)
.put("trigger", ImmutableMap.<String, Object>builder()
.put("triggered_time", now)
@ -134,7 +132,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
assertThat(actualEmail.textBody(), is(textBody == null ? null : textBody.getText()));
assertThat(actualEmail.htmlBody(), is(htmlBody == null ? null : htmlBody.getText()));
if (attachPayload) {
assertThat(actualEmail.attachments(), hasKey("payload"));
assertThat(actualEmail.attachments(), hasKey("data"));
}
}
@ -151,13 +149,13 @@ public class EmailActionTests extends ElasticsearchTestCase {
Template subject = randomBoolean() ? new Template("_subject") : null;
Template textBody = randomBoolean() ? new Template("_text_body") : null;
Template htmlBody = randomBoolean() ? new Template("_text_html") : null;
boolean attachPayload = randomBoolean();
boolean attachData = randomBoolean();
XContentBuilder builder = jsonBuilder().startObject()
.field("account", "_account")
.field("profile", profile.name())
.field("user", "_user")
.field("password", "_passwd")
.field("attach_payload", attachPayload)
.field("attach_data", attachData)
.field("from", "from@domain")
.field("priority", priority.name());
if (to != null) {
@ -219,7 +217,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
assertThat(executable, notNullValue());
assertThat(executable.action().getAccount(), is("_account"));
assertThat(executable.action().isAttachPayload(), is(attachPayload));
assertThat(executable.action().getAttachData(), is(attachData));
assertThat(executable.action().getAuth(), notNullValue());
assertThat(executable.action().getAuth().user(), is("_user"));
assertThat(executable.action().getAuth().password(), is("_passwd".toCharArray()));

View File

@ -20,14 +20,12 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.actions.ActionException;
import org.elasticsearch.watcher.actions.email.service.Attachment;
import org.elasticsearch.watcher.execution.TriggeredExecutionContext;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.execution.Wid;
import org.elasticsearch.watcher.support.template.Template;
import org.elasticsearch.watcher.support.template.TemplateEngine;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
@ -66,8 +64,9 @@ public class LoggingActionTests extends ElasticsearchTestCase {
final Map<String, Object> expectedModel = ImmutableMap.<String, Object>builder()
.put("ctx", ImmutableMap.builder()
.put("execution_time", now)
.put("watch_id", "_watch_name")
.put("watch_id", "_watch_id")
.put("payload", ImmutableMap.of())
.put("metadata", ImmutableMap.of())
.put("trigger", ImmutableMap.builder()
.put("scheduled_time", now)
.put("triggered_time", now)
@ -76,15 +75,14 @@ public class LoggingActionTests extends ElasticsearchTestCase {
.build();
String text = randomAsciiOfLength(10);
System.out.println("**** text: " + text);
Template template = new Template(text);
LoggingAction action = new LoggingAction(template, level, "_category");
ExecutableLoggingAction executable = new ExecutableLoggingAction(action, logger, actionLogger, engine);
when(engine.render(template, expectedModel)).thenReturn(text);
Watch watch = mock(Watch.class);
when(watch.name()).thenReturn("_watch_name");
WatchExecutionContext ctx = new TriggeredExecutionContext(watch, now, new ScheduleTriggerEvent(now, now));
WatchExecutionContext ctx = WatcherTestUtils.mockExecutionContextBuilder("_watch_id")
.time(now)
.buildMock();
LoggingAction.Result result = executable.execute("_id", ctx, new Payload.Simple());
verifyLogger(actionLogger, level, text);

View File

@ -0,0 +1,57 @@
/*
* 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;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.test.WatcherTestUtils;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.junit.Test;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.hamcrest.Matchers.*;
/**
*
*/
public class VariablesTests extends ElasticsearchTestCase {
@Test
public void testCreateCtxModel() throws Exception {
DateTime scheduledTime = DateTime.now(UTC);
DateTime triggeredTime = scheduledTime.plusMillis(50);
DateTime executionTime = triggeredTime.plusMillis(50);
Payload payload = new Payload.Simple(ImmutableMap.<String, Object>builder().put("payload_key", "payload_value").build());
Map<String, Object> metatdata = ImmutableMap.<String, Object>builder().put("metadata_key", "metadata_value").build();
TriggerEvent event = new ScheduleTriggerEvent(triggeredTime, scheduledTime);
WatchExecutionContext wec = WatcherTestUtils.mockExecutionContextBuilder("_watch_id")
.executionTime(executionTime)
.triggerEvent(event)
.payload(payload)
.metadata(metatdata)
.buildMock();
Map<String, Object> model = Variables.createCtxModel(wec, payload);
assertThat(model, notNullValue());
assertThat(model, hasKey(Variables.CTX));
assertThat(model.get(Variables.CTX), instanceOf(Map.class));
assertThat(model.size(), is(1));
Map<String, Object> ctx = (Map<String, Object>) model.get(Variables.CTX);
assertThat(ctx, hasEntry(Variables.WATCH_ID, (Object) "_watch_id"));
assertThat(ctx, hasEntry(Variables.EXECUTION_TIME, (Object) executionTime));
assertThat(ctx, hasEntry(Variables.TRIGGER, (Object) event.data()));
assertThat(ctx, hasEntry(Variables.PAYLOAD, (Object) payload.data()));
assertThat(ctx, hasEntry(Variables.METADATA, (Object) metatdata));
assertThat(ctx.size(), is(5));
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.test;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.joda.time.DateTime;
import org.elasticsearch.watcher.execution.WatchExecutionContext;
import org.elasticsearch.watcher.execution.Wid;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
import java.util.Collections;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
*
*/
public class WatchExecutionContextMockBuilder {
private final WatchExecutionContext ctx;
private final Watch watch;
public WatchExecutionContextMockBuilder(String watchId) {
ctx = mock(WatchExecutionContext.class);
watch = mock(Watch.class);
when(watch.name()).thenReturn(watchId);
when(ctx.watch()).thenReturn(watch);
payload(Collections.<String, Object>emptyMap());
metadata(Collections.<String, Object>emptyMap());
time(DateTime.now(UTC));
}
public WatchExecutionContextMockBuilder wid(Wid wid) {
when(ctx.id()).thenReturn(wid);
return this;
}
public WatchExecutionContextMockBuilder payload(String key, Object value) {
return payload(new Payload.Simple(MapBuilder.<String, Object>newMapBuilder().put(key, value).map()));
}
public WatchExecutionContextMockBuilder payload(Map<String, Object> payload) {
return payload(new Payload.Simple(payload));
}
public WatchExecutionContextMockBuilder payload(Payload payload) {
when(ctx.payload()).thenReturn(payload);
return this;
}
public WatchExecutionContextMockBuilder time(DateTime time) {
return executionTime(time).triggerEvent(new ScheduleTriggerEvent(time, time));
}
public WatchExecutionContextMockBuilder executionTime(DateTime time) {
when(ctx.executionTime()).thenReturn(time);
return this;
}
public WatchExecutionContextMockBuilder triggerEvent(TriggerEvent event) {
when(ctx.triggerEvent()).thenReturn(event);
return this;
}
public WatchExecutionContextMockBuilder metadata(Map<String, Object> metadata) {
when(watch.metadata()).thenReturn(metadata);
return this;
}
public WatchExecutionContextMockBuilder metadata(String key, String value) {
return metadata(MapBuilder.<String, Object>newMapBuilder().put(key, value).map());
}
public WatchExecutionContext buildMock() {
return ctx;
}
}

View File

@ -47,7 +47,6 @@ import org.elasticsearch.watcher.transform.SearchTransform;
import org.elasticsearch.watcher.trigger.TriggerEvent;
import org.elasticsearch.watcher.trigger.schedule.CronSchedule;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTrigger;
import org.elasticsearch.watcher.trigger.schedule.ScheduleTriggerEvent;
import org.elasticsearch.watcher.watch.Payload;
import org.elasticsearch.watcher.watch.Watch;
@ -57,7 +56,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.joda.time.DateTimeZone.UTC;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.mockito.Mockito.mock;
@ -97,23 +95,29 @@ public final class WatcherTestUtils {
return new Payload.Simple(key, value);
}
public static WatchExecutionContextMockBuilder mockExecutionContextBuilder(String watchId) {
return new WatchExecutionContextMockBuilder(watchId);
}
public static WatchExecutionContext mockExecutionContext(String watchId, Payload payload) {
return mockExecutionContext(watchId, DateTime.now(UTC), payload);
return mockExecutionContextBuilder(watchId)
.payload(payload)
.buildMock();
}
public static WatchExecutionContext mockExecutionContext(String watchName, DateTime time, Payload payload) {
return mockExecutionContext(watchName, time, new ScheduleTriggerEvent(time, time), payload);
public static WatchExecutionContext mockExecutionContext(String watchId, DateTime time, Payload payload) {
return mockExecutionContextBuilder(watchId)
.payload(payload)
.time(time)
.buildMock();
}
public static WatchExecutionContext mockExecutionContext(String watchName, DateTime executionTime, TriggerEvent event, Payload payload) {
WatchExecutionContext ctx = mock(WatchExecutionContext.class);
when(ctx.executionTime()).thenReturn(executionTime);
when(ctx.triggerEvent()).thenReturn(event);
Watch watch = mock(Watch.class);
when(watch.name()).thenReturn(watchName);
when(ctx.watch()).thenReturn(watch);
when(ctx.payload()).thenReturn(payload);
return ctx;
public static WatchExecutionContext mockExecutionContext(String watchId, DateTime executionTime, TriggerEvent event, Payload payload) {
return mockExecutionContextBuilder(watchId)
.payload(payload)
.executionTime(executionTime)
.triggerEvent(event)
.buildMock();
}

View File

@ -155,7 +155,7 @@ public class BootStrapTests extends AbstractWatcherIntegrationTests {
new ExecutableScriptCondition(new ScriptCondition(new Script("return true")), logger, scriptService()),
new SearchTransform(logger, scriptService(), ClientProxy.of(client()), searchRequest),
new ExecutableActions(new ArrayList<ActionWrapper>()),
null, // metatdata
null, // metadata
new TimeValue(0),
new Watch.Status());
XContentBuilder jsonBuilder = jsonBuilder();