From 02ba76fe216476e5d782b9eada480b74081835c2 Mon Sep 17 00:00:00 2001 From: uboness Date: Mon, 20 Apr 2015 20:56:56 +0200 Subject: [PATCH] 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@c5dde855d2f001d2bf672916385c3a6354749248 --- .../watcher/actions/email/EmailAction.java | 22 ++--- .../actions/email/ExecutableEmailAction.java | 4 +- .../execution/WatchExecutionContext.java | 3 +- .../watcher/support/Variables.java | 15 ++-- .../actions/email/EmailActionTests.java | 38 ++++---- .../actions/logging/LoggingActionTests.java | 14 ++- .../watcher/support/VariablesTests.java | 57 ++++++++++++ .../WatchExecutionContextMockBuilder.java | 86 +++++++++++++++++++ .../watcher/test/WatcherTestUtils.java | 32 ++++--- .../test/integration/BootStrapTests.java | 2 +- 10 files changed, 206 insertions(+), 67 deletions(-) create mode 100644 src/test/java/org/elasticsearch/watcher/support/VariablesTests.java create mode 100644 src/test/java/org/elasticsearch/watcher/test/WatchExecutionContextMockBuilder.java diff --git a/src/main/java/org/elasticsearch/watcher/actions/email/EmailAction.java b/src/main/java/org/elasticsearch/watcher/actions/email/EmailAction.java index 0940dd35e39..97a864c59b5 100644 --- a/src/main/java/org/elasticsearch/watcher/actions/email/EmailAction.java +++ b/src/main/java/org/elasticsearch/watcher/actions/email/EmailAction.java @@ -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"); diff --git a/src/main/java/org/elasticsearch/watcher/actions/email/ExecutableEmailAction.java b/src/main/java/org/elasticsearch/watcher/actions/email/ExecutableEmailAction.java index 9632cf2c134..90fd8e98db2 100644 --- a/src/main/java/org/elasticsearch/watcher/actions/email/ExecutableEmailAction.java +++ b/src/main/java/org/elasticsearch/watcher/actions/email/ExecutableEmailAction.java @@ -36,8 +36,8 @@ public class ExecutableEmailAction extends ExecutableAction createCtxModel(WatchExecutionContext ctx, Payload payload) { - return createCtxModel(ctx.watch().name(), ctx.executionTime(), ctx.triggerEvent(), payload); - } - - public static Map createCtxModel(String watchName, DateTime executionTime, TriggerEvent triggerEvent, Payload payload) { Map 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 model = new HashMap<>(); model.put(CTX, vars); return model; } + } diff --git a/src/test/java/org/elasticsearch/watcher/actions/email/EmailActionTests.java b/src/test/java/org/elasticsearch/watcher/actions/email/EmailActionTests.java index 52599c481b8..2b1782365ef 100644 --- a/src/test/java/org/elasticsearch/watcher/actions/email/EmailActionTests.java +++ b/src/test/java/org/elasticsearch/watcher/actions/email/EmailActionTests.java @@ -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 data = new HashMap<>(); - Payload payload = new Payload() { - @Override - public Map data() { - return data; - } + Map data = new HashMap<>(); + Payload payload = new Payload.Simple(data); - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder.map(data); - } - }; + Map metadata = MapBuilder.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 expectedModel = ImmutableMap.builder() .put("ctx", ImmutableMap.builder() .put("watch_id", "watch1") .put("payload", data) + .put("metadata", metadata) .put("execution_time", now) .put("trigger", ImmutableMap.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())); diff --git a/src/test/java/org/elasticsearch/watcher/actions/logging/LoggingActionTests.java b/src/test/java/org/elasticsearch/watcher/actions/logging/LoggingActionTests.java index c70b04a4f3d..2c65eaa0888 100644 --- a/src/test/java/org/elasticsearch/watcher/actions/logging/LoggingActionTests.java +++ b/src/test/java/org/elasticsearch/watcher/actions/logging/LoggingActionTests.java @@ -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 expectedModel = ImmutableMap.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); diff --git a/src/test/java/org/elasticsearch/watcher/support/VariablesTests.java b/src/test/java/org/elasticsearch/watcher/support/VariablesTests.java new file mode 100644 index 00000000000..db5f1500630 --- /dev/null +++ b/src/test/java/org/elasticsearch/watcher/support/VariablesTests.java @@ -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.builder().put("payload_key", "payload_value").build()); + Map metatdata = ImmutableMap.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 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 ctx = (Map) 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)); + } +} diff --git a/src/test/java/org/elasticsearch/watcher/test/WatchExecutionContextMockBuilder.java b/src/test/java/org/elasticsearch/watcher/test/WatchExecutionContextMockBuilder.java new file mode 100644 index 00000000000..ce6423ece1d --- /dev/null +++ b/src/test/java/org/elasticsearch/watcher/test/WatchExecutionContextMockBuilder.java @@ -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.emptyMap()); + metadata(Collections.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.newMapBuilder().put(key, value).map())); + } + + public WatchExecutionContextMockBuilder payload(Map 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 metadata) { + when(watch.metadata()).thenReturn(metadata); + return this; + } + + public WatchExecutionContextMockBuilder metadata(String key, String value) { + return metadata(MapBuilder.newMapBuilder().put(key, value).map()); + } + + public WatchExecutionContext buildMock() { + return ctx; + } +} diff --git a/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java b/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java index 30b7a54ff21..fcfe1bac72e 100644 --- a/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java +++ b/src/test/java/org/elasticsearch/watcher/test/WatcherTestUtils.java @@ -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(); } diff --git a/src/test/java/org/elasticsearch/watcher/test/integration/BootStrapTests.java b/src/test/java/org/elasticsearch/watcher/test/integration/BootStrapTests.java index d793bf2afd9..f2d6e95234e 100644 --- a/src/test/java/org/elasticsearch/watcher/test/integration/BootStrapTests.java +++ b/src/test/java/org/elasticsearch/watcher/test/integration/BootStrapTests.java @@ -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()), - null, // metatdata + null, // metadata new TimeValue(0), new Watch.Status()); XContentBuilder jsonBuilder = jsonBuilder();