diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java
index 81bb3d6772..defc3fed0a 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java
@@ -49,11 +49,10 @@ import java.util.concurrent.TimeUnit;
configClazz = TbMsgDeduplicationNodeConfiguration.class,
nodeDescription = "Deduplicate messages within the same originator entity for a configurable period " +
"based on a specified deduplication strategy.",
- nodeDetails = "Rule node allows you to select one of the following strategy to deduplicate messages:
" +
- "FIRST - return first message that arrived during deduplication period.
" +
- "LAST - return last message that arrived during deduplication period.
" +
- "ALL - return all messages as a single JSON array message. " +
- "Where each element represents object with msg and metadata inner properties.
",
+ nodeDetails = "Deduplication strategy:
- FIRST - return first message that arrived during deduplication period.
" +
+ "- LAST - return last message that arrived during deduplication period.
" +
+ "- ALL - return all messages as a single JSON array message. " +
+ "Where each element represents object with msg and metadata inner properties.
",
icon = "content_copy",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeMsgDeduplicationConfig"
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
index a52641bedc..f16d5bc666 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.rule.engine.mail;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleNode;
@@ -30,9 +30,9 @@ import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -42,8 +42,11 @@ import java.util.Map;
name = "to email",
configClazz = TbMsgToEmailNodeConfiguration.class,
nodeDescription = "Transforms message to email message",
- nodeDetails = "Transforms message to email message by populating email fields using values derived from message metadata. " +
- "Set 'SEND_EMAIL' output message type.",
+ nodeDetails = "Transforms message to email message. All email fields support templatization. " +
+ "Supports adding inline base64 images to the mail body from message metadata by checking for the images metadata key " +
+ "that should represent a map of entries: contentId to based64 image value.
" +
+ "Set SEND_EMAIL output message type if transformation completed successfully.
" +
+ "Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeToEmailConfig",
icon = "email"
@@ -54,12 +57,12 @@ public class TbMsgToEmailNode implements TbNode {
private static final String DYNAMIC = "dynamic";
private TbMsgToEmailNodeConfiguration config;
- private boolean isDynamicHtmlTemplate;
+ private boolean dynamicMailBodyType;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbMsgToEmailNodeConfiguration.class);
- this.isDynamicHtmlTemplate = DYNAMIC.equals(this.config.getMailBodyType());
+ this.dynamicMailBodyType = DYNAMIC.equals(this.config.getMailBodyType());
}
@Override
@@ -74,24 +77,22 @@ public class TbMsgToEmailNode implements TbNode {
}
}
- private TbMsg buildEmailMsg(TbContext ctx, TbMsg msg, TbEmail email) throws JsonProcessingException {
+ private TbMsg buildEmailMsg(TbContext ctx, TbMsg msg, TbEmail email) {
String emailJson = JacksonUtil.toString(email);
return ctx.transformMsg(msg, TbMsgType.SEND_EMAIL, msg.getOriginator(), msg.getMetaData().copy(), emailJson);
}
- private TbEmail convert(TbMsg msg) throws IOException {
+ private TbEmail convert(TbMsg msg) {
TbEmail.TbEmailBuilder builder = TbEmail.builder();
- builder.from(fromTemplate(this.config.getFromTemplate(), msg));
- builder.to(fromTemplate(this.config.getToTemplate(), msg));
- builder.cc(fromTemplate(this.config.getCcTemplate(), msg));
- builder.bcc(fromTemplate(this.config.getBccTemplate(), msg));
- if(isDynamicHtmlTemplate) {
- builder.html(Boolean.parseBoolean(fromTemplate(this.config.getIsHtmlTemplate(), msg)));
- } else {
- builder.html(Boolean.parseBoolean(this.config.getMailBodyType()));
- }
- builder.subject(fromTemplate(this.config.getSubjectTemplate(), msg));
- builder.body(fromTemplate(this.config.getBodyTemplate(), msg));
+ builder.from(fromTemplate(config.getFromTemplate(), msg));
+ builder.to(fromTemplate(config.getToTemplate(), msg));
+ builder.cc(fromTemplate(config.getCcTemplate(), msg));
+ builder.bcc(fromTemplate(config.getBccTemplate(), msg));
+ String htmlStr = dynamicMailBodyType ?
+ fromTemplate(config.getIsHtmlTemplate(), msg) : config.getMailBodyType();
+ builder.html(Boolean.parseBoolean(htmlStr));
+ builder.subject(fromTemplate(config.getSubjectTemplate(), msg));
+ builder.body(fromTemplate(config.getBodyTemplate(), msg));
String imagesStr = msg.getMetaData().getValue(IMAGES);
if (!StringUtils.isEmpty(imagesStr)) {
Map imgMap = JacksonUtil.fromString(imagesStr, new TypeReference>() {});
@@ -101,11 +102,7 @@ public class TbMsgToEmailNode implements TbNode {
}
private String fromTemplate(String template, TbMsg msg) {
- if (!StringUtils.isEmpty(template)) {
- return TbNodeUtils.processPattern(template, msg);
- } else {
- return null;
- }
+ return StringUtils.isNotEmpty(template) ? TbNodeUtils.processPattern(template, msg) : null;
}
}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java
index 7b8ddefa7f..f31b5a3893 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java
@@ -19,7 +19,7 @@ import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
@Data
-public class TbMsgToEmailNodeConfiguration implements NodeConfiguration {
+public class TbMsgToEmailNodeConfiguration implements NodeConfiguration {
private String fromTemplate;
private String toTemplate;
@@ -28,16 +28,16 @@ public class TbMsgToEmailNodeConfiguration implements NodeConfiguration {
private String subjectTemplate;
private String bodyTemplate;
private String isHtmlTemplate;
- private String mailBodyType;
+ private String mailBodyType; // Plain Text -> false. HTML - true. Dynamic - value used from isHtmlTemplate.
@Override
public TbMsgToEmailNodeConfiguration defaultConfiguration() {
- TbMsgToEmailNodeConfiguration configuration = new TbMsgToEmailNodeConfiguration();
- configuration.fromTemplate = "info@testmail.org";
- configuration.toTemplate = "${userEmail}";
- configuration.subjectTemplate = "Device ${deviceType} temperature high";
- configuration.bodyTemplate = "Device ${deviceName} has high temperature ${temp}";
- configuration.mailBodyType = "false";
+ var configuration = new TbMsgToEmailNodeConfiguration();
+ configuration.setFromTemplate("info@testmail.org");
+ configuration.setToTemplate("${userEmail}");
+ configuration.setSubjectTemplate("Device ${deviceType} temperature high");
+ configuration.setBodyTemplate("Device ${deviceName} has high temperature $[temperature]");
+ configuration.setMailBodyType("false");
return configuration;
}
}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java
index 9efc051e2b..a26cec57ee 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java
@@ -43,13 +43,15 @@ import java.util.NoSuchElementException;
type = ComponentType.TRANSFORMATION,
name = "change originator",
configClazz = TbChangeOriginatorNodeConfiguration.class,
- nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity/Alarm Originator/Entity by name pattern",
- nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
- "If multiple related entities are found, only first entity is used as new originator, other entities are discarded.
" +
- "Alarm Originator might be found only if the original Originator is Alarm entity.
" +
- "Entity by name pattern lookup only if found only in case original Originator is Alarm entity.
" +
- "Lookup of an entity by name pattern requires selection of entity type and name pattern to be specified in the configuration. " +
- "Allowed entity types to select: 'DEVICE', 'ASSET', 'ENTITY_VIEW', 'EDGE' and 'USER'.
" +
+ nodeDescription = "Change message originator to Tenant/Customer/Related Entity/Alarm Originator/Entity by name pattern.",
+ nodeDetails = "Configuration: - Tenant - use current tenant as new originator.
" +
+ "- Customer - use customer of incoming message originator as new originator. " +
+ "Only for originators with one of the following type: 'User', 'Asset', 'Device' that assigned to customers.
" +
+ "- Related Entity - use related entity as new originator. Lookup based on configured relation direction and relation type. " +
+ "If multiple related entities are found, only first entity is used as new originator, other entities are discarded.
" +
+ "- Alarm Originator - use alarm originator as new originator. Only if incoming message originator is alarm entity.
" +
+ "- Entity by name pattern - new originator lookup requires selection one of the following entity types: " +
+ "'Device', 'Asset', 'Entity View', 'Edge' or 'User' and input of name pattern.
" +
"Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeChangeOriginatorConfig",
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java
index 93b5ecb5d7..25467caf5a 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java
@@ -44,7 +44,8 @@ import java.util.stream.Collectors;
configClazz = TbCopyKeysNodeConfiguration.class,
nodeDescription = "Copies key-values from message to message metadata or vice-versa.",
nodeDetails = "Fetches key-values from message or message metadata based on the keys list specified in the configuration " +
- "and copies them into message metadata or message. Keys that are absent in the fetch source will be ignored. " +
+ "and copies them into message metadata or into message itself in accordance with the fetch source. " +
+ "Keys that are absent in the fetch source will be ignored. " +
"Use regular expression(s) as a key(s) to copy keys by pattern.
" +
"Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java
index 4666016f78..15ed71b0ae 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java
@@ -43,7 +43,7 @@ import java.util.stream.Collectors;
name = "delete key-values",
version = 1,
configClazz = TbDeleteKeysNodeConfiguration.class,
- nodeDescription = "Removes key-values from message or metadata.",
+ nodeDescription = "Removes key-values from message or message metadata.",
nodeDetails = "Removes key-values from message or message metadata based on the keys list specified in the configuration. " +
"Use regular expression(s) as a key(s) to remove keys by pattern.
" +
"Output connections: Success, Failure.",
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java
index 9454bd51c4..6e00b98e3e 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java
@@ -38,9 +38,8 @@ import java.util.concurrent.ExecutionException;
name = "json path",
configClazz = TbJsonPathNodeConfiguration.class,
nodeDescription = "Transforms incoming message body using JSONPath expression.",
- nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.
" +
- "'$' represents the root object or array.
" +
- "If JSONPath expression evaluation failed, incoming message routes via Failure chain.
" +
+ nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure. " +
+ "$ represents the root object or array.
" +
"Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
icon = "functions",
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java
index d6e4e0dc34..7305c3ee78 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java
@@ -39,9 +39,9 @@ import java.util.concurrent.ExecutionException;
name = "rename keys",
version = 1,
configClazz = TbRenameKeysNodeConfiguration.class,
- nodeDescription = "Renames message or message metadata key names to the new key names selected in the key mapping.",
- nodeDetails = "If the key name that is selected in the key mapping is missed in the " +
- "selected source(message or message metadata), it will be ignored.
" +
+ nodeDescription = "Renames message or message metadata key names.",
+ nodeDetails = "Renames key names in the message or message metadata based on the provided key names mapping. " +
+ "If key to rename doesn't exists in the specified source(message or message metadata) it will be ignored.
" +
"Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeRenameKeysConfig",
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java
index 7a3e53c69c..7a9c812ebc 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java
@@ -40,9 +40,8 @@ import java.util.concurrent.ExecutionException;
name = "split array msg",
configClazz = EmptyNodeConfiguration.class,
nodeDescription = "Split array message into several messages",
- nodeDetails = "Split the array fetched from the message. If the msg data is not a JSON array returns the "
- + "incoming message as outbound message with Failure chain, otherwise returns "
- + "inner objects of the extracted array as separate messages via Success chain.
" +
+ nodeDetails = "Split array message and returns inner objects as separate messages. " +
+ "Message metadata and type for each output message will be taken from original message.
" +
"Output connections: Success, Failure.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
icon = "content_copy",
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
index 0cff412d39..79549edbd7 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
@@ -34,9 +34,9 @@ import java.util.List;
configClazz = TbTransformMsgNodeConfiguration.class,
nodeDescription = "Change Message payload, Metadata or Message type using JavaScript",
nodeDetails = "JavaScript function receive 3 input parameters
" +
- "metadata - is a Message metadata.
" +
- "msg - is a Message payload.
" +
- "msgType - is a Message type.
" +
+ "msg - is a message payload.
" +
+ "metadata - is a message metadata.
" +
+ "msgType - is a message type.
" +
"Should return the following structure:
" +
"{ msg: new payload,
   metadata: new metadata,
   msgType: new msgType }
" +
"All fields in resulting object are optional and will be taken from original message if not specified.
" +
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java
index 9579c73058..5a19fa860f 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java
@@ -44,8 +44,7 @@ public class EntitiesCustomerIdAsyncLoader {
}
private static ListenableFuture toCustomerIdAsync(TbContext ctx, ListenableFuture future) {
- return Futures.transformAsync(future, in -> in != null ? Futures.immediateFuture(in.getCustomerId())
- : Futures.immediateFuture(null), ctx.getDbCallbackExecutor());
+ return Futures.transform(future, in -> in != null ? in.getCustomerId() : null, ctx.getDbCallbackExecutor());
}
}
diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java
index bf4d090c1c..4f1b0488d7 100644
--- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java
+++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java
@@ -15,12 +15,15 @@
*/
package org.thingsboard.rule.engine.mail;
-import com.datastax.oss.driver.api.core.uuid.Uuids;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbEmail;
@@ -28,85 +31,135 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
-import org.thingsboard.server.common.data.id.RuleChainId;
-import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
-import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
-import java.io.IOException;
+import java.util.UUID;
+import java.util.stream.Stream;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-@RunWith(MockitoJUnitRunner.class)
public class TbMsgToEmailNodeTest {
- private TbMsgToEmailNode emailNode;
+ private static final int EXPECTED_TEMPERATURE = 30;
+ private static final String EXPECTED_DEVICE_NAME = "TH-001";
+ private static final String EXPECTED_DEVICE_TYPE = "thermostat";
+ private static final String EXPECTED_SUBJECT = "Device " + EXPECTED_DEVICE_TYPE + " temperature high";
+ private static final String EXPECTED_BODY = "Device " + EXPECTED_DEVICE_NAME + " has high temperature " + EXPECTED_TEMPERATURE;
+ private static final String EXPECTED_TO_EMAIL = "user@email.io";
+ private static final String DYNAMIC_MAIL_BODY_TYPE = "dynamic";
+
+ private EntityId originator;
+ private TbMsgToEmailNode node;
+ private TbMsgToEmailNodeConfiguration config;
+
+ private TbContext ctxMock;
+
+ @BeforeEach
+ void setUp() throws TbNodeException {
+ ctxMock = mock(TbContext.class);
+ originator = new DeviceId(UUID.randomUUID());
+ config = new TbMsgToEmailNodeConfiguration().defaultConfiguration();
+ node = new TbMsgToEmailNode();
+ node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
+ }
- @Mock
- private TbContext ctx;
+ @AfterEach
+ void tearDown() {
+ node.destroy();
+ }
- private EntityId originator = new DeviceId(Uuids.timeBased());
- private TbMsgMetaData metaData = new TbMsgMetaData();
- private String rawJson = "{\"name\": \"temp\", \"passed\": 5 , \"complex\": {\"val\":12, \"count\":100}}";
+ @Test
+ void givenDefaultConfig_whenVerify_thenOK() {
+ // GIVEN-WHEN-THEN
+ assertThat(config.getFromTemplate()).isEqualTo("info@testmail.org");
+ assertThat(config.getToTemplate()).isEqualTo("${userEmail}");
+ assertThat(config.getSubjectTemplate()).isEqualTo("Device ${deviceType} temperature high");
+ assertThat(config.getBodyTemplate()).isEqualTo("Device ${deviceName} has high temperature $[temperature]");
+ }
- private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
- private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased());
+ static Stream MailBodyTypeTestConfig() {
+ return Stream.of(
+ new MailBodyTypeTestConfig(false, "false", null),
+ new MailBodyTypeTestConfig(false, null, null),
+ new MailBodyTypeTestConfig(false, DYNAMIC_MAIL_BODY_TYPE, "false"),
+ new MailBodyTypeTestConfig(true, DYNAMIC_MAIL_BODY_TYPE, "true"),
+ new MailBodyTypeTestConfig(true, "true", null)
+ );
+ }
- @Test
- public void msgCanBeConverted() {
- initWithScript();
- metaData.putValue("username", "oreo");
- metaData.putValue("userEmail", "user@email.io");
- metaData.putValue("name", "temp");
- metaData.putValue("passed", "5");
- metaData.putValue("count", "100");
- TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
-
- emailNode.onMsg(ctx, msg);
-
- ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
- ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
- ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
- ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
- ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class);
- verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
-
-
- assertEquals(TbMsgType.SEND_EMAIL, typeCaptor.getValue());
- assertEquals(originator, originatorCaptor.getValue());
- assertEquals("oreo", metadataCaptor.getValue().getValue("username"));
- assertNotSame(metaData, metadataCaptor.getValue());
-
- TbEmail actual = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), TbEmail.class);
-
- TbEmail expected = TbEmail.builder()
- .from("test@mail.org")
- .to("user@email.io")
- .subject("Hi oreo there")
- .body("temp is to high. Current 5 and 100")
+ @ParameterizedTest
+ @MethodSource("MailBodyTypeTestConfig")
+ public void givenMailBodyTypeTestConfig_whenOnMsg_thenVerify(MailBodyTypeTestConfig testConfig) throws TbNodeException {
+ // GIVEN
+ String mailBodyType = testConfig.getMailBodyType();
+ config.setMailBodyType(mailBodyType);
+ if (DYNAMIC_MAIL_BODY_TYPE.equals(mailBodyType)) {
+ config.setIsHtmlTemplate("${html}");
+ }
+ node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
+
+ var md = new TbMsgMetaData();
+ md.putValue("userEmail", EXPECTED_TO_EMAIL);
+ md.putValue("deviceType", EXPECTED_DEVICE_TYPE);
+ md.putValue("deviceName", EXPECTED_DEVICE_NAME);
+ if (testConfig.getMdValue() != null) {
+ md.putValue("html", testConfig.getMdValue());
+ }
+
+ var msgDataStr = "{\"temperature\": " + EXPECTED_TEMPERATURE + "}";
+ TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, md, msgDataStr);
+
+ // WHEN
+ node.onMsg(ctxMock, msg);
+
+ // THEN
+ var msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
+ var typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
+ var originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
+ var metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
+ var dataCaptor = ArgumentCaptor.forClass(String.class);
+ verify(ctxMock, times(1)).transformMsg(
+ msgCaptor.capture(),
+ typeCaptor.capture(),
+ originatorCaptor.capture(),
+ metadataCaptor.capture(),
+ dataCaptor.capture()
+ );
+ verify(ctxMock, never()).tellFailure(any(), any());
+
+ Assertions.assertEquals(TbMsgType.SEND_EMAIL, typeCaptor.getValue());
+ Assertions.assertEquals(originator, originatorCaptor.getValue());
+ Assertions.assertNotSame(md, metadataCaptor.getValue());
+
+ var actual = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), TbEmail.class);
+ var expected = getExpectedTbEmail(testConfig.isExpectedHtmlValue());
+
+ Assertions.assertEquals(expected, actual);
+ }
+
+ private TbEmail getExpectedTbEmail(boolean html) {
+ return TbEmail.builder()
+ .from(config.getFromTemplate())
+ .to(EXPECTED_TO_EMAIL)
+ .subject(EXPECTED_SUBJECT)
+ .body(EXPECTED_BODY)
+ .html(html)
.build();
- assertEquals(expected, actual);
}
- private void initWithScript() {
- try {
- TbMsgToEmailNodeConfiguration config = new TbMsgToEmailNodeConfiguration();
- config.setFromTemplate("test@mail.org");
- config.setToTemplate("${userEmail}");
- config.setSubjectTemplate("Hi ${username} there");
- config.setBodyTemplate("${name} is to high. Current ${passed} and ${count}");
- config.setMailBodyType("false");
- TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
-
- emailNode = new TbMsgToEmailNode();
- emailNode.init(ctx, nodeConfiguration);
- } catch (TbNodeException ex) {
- throw new IllegalStateException(ex);
- }
+ @Data
+ @RequiredArgsConstructor
+ static class MailBodyTypeTestConfig {
+ private final boolean expectedHtmlValue;
+ private final String mailBodyType;
+ private final String mdValue;
}
}