thingsboard-memoizeit
Changes
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java 2(+0 -2)
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java 2(+1 -1)
application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java 21(+0 -21)
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java 4(+2 -2)
application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java 151(+151 -0)
common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java 2(+1 -1)
common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java 2(+1 -1)
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java 10(+7 -3)
rule-engine/rule-engine-components/pom.xml 10(+10 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/EmailPojo.java 32(+32 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/RuleVelocityUtils.java 68(+68 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java 114(+114 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java 41(+41 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java 89(+89 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java 32(+32 -0)
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java 8(+4 -4)
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java 85(+18 -67)
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java 54(+12 -42)
Details
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index b2496c0..7c7747c 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.ListeningExecutor;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
@@ -58,6 +59,8 @@ import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
+import org.thingsboard.server.service.mail.MailExecutorService;
+import org.thingsboard.server.service.script.JsExecutorService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import java.io.IOException;
@@ -164,7 +167,15 @@ public class ActorSystemContext {
@Autowired
@Getter
- private ListeningExecutor jsExecutor;
+ private JsExecutorService jsExecutor;
+
+ @Autowired
+ @Getter
+ private MailExecutorService mailExecutor;
+
+ @Autowired
+ @Getter
+ private MailService mailService;
@Value("${actors.session.sync.timeout}")
@Getter
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index 9b6e4ba..4ff75bf 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -18,10 +18,7 @@ package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorRef;
import akka.actor.Cancellable;
import com.google.common.base.Function;
-import org.thingsboard.rule.engine.api.ListeningExecutor;
-import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
-import org.thingsboard.rule.engine.api.ScriptEngine;
-import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -136,6 +133,11 @@ class DefaultTbContext implements TbContext {
}
@Override
+ public ListeningExecutor getMailExecutor() {
+ return mainCtx.getMailExecutor();
+ }
+
+ @Override
public ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames) {
return new NashornJsEngine(script, functionName, argNames);
}
@@ -194,4 +196,9 @@ class DefaultTbContext implements TbContext {
public RelationService getRelationService() {
return mainCtx.getRelationService();
}
+
+ @Override
+ public MailService getMailService() {
+ return mainCtx.getMailService();
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 2952529..24c533c 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -37,7 +36,6 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
index d44c50e..2e5050a 100644
--- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
@@ -17,8 +17,8 @@ package org.thingsboard.server.config;
import java.util.Map;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.controller.plugin.TbWebSocketHandler;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.springframework.context.annotation.Bean;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
index e9a6ba3..5a43125 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
@@ -20,8 +20,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.dao.settings.AdminSettingsService;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.service.mail.MailService;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.service.update.UpdateService;
import org.thingsboard.server.service.update.model.UpdateMessage;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 1959f4e..81bcf7e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -23,8 +23,8 @@ import org.thingsboard.server.common.data.alarm.*;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index 9b43913..0e348f9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -33,8 +33,8 @@ import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
index 75bcf2a..e8685c7 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
@@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.UUID;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
index ef38d80..96ff516 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
@@ -28,9 +28,9 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.service.mail.MailService;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 3294d5f..c7574e8 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -15,12 +15,9 @@
*/
package org.thingsboard.server.controller;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -30,7 +27,6 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink;
@@ -61,9 +57,9 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.security.model.SecurityUser;
diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
index 7a43cd3..6313d61 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
@@ -19,7 +19,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.HashSet;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index b164702..7763f3c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index d2952a1..4ec2bba 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -27,9 +27,7 @@ import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
-import org.thingsboard.server.dao.exception.IncorrectParameterException;
-import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.HashSet;
import java.util.Set;
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index bceea54..1f4b02a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
-import org.thingsboard.server.common.data.audit.ActionStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
@@ -35,8 +34,8 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
index 03054df..3a3b78b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -24,8 +24,8 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java
index 331b15e..f67f113 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java
@@ -24,8 +24,8 @@ import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
index 045835e..8d25db3 100644
--- a/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
@@ -17,31 +17,10 @@ package org.thingsboard.server.controller.plugin;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.RequestEntity;
-import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.context.request.async.DeferredResult;
-import org.thingsboard.server.actors.service.ActorService;
-import org.thingsboard.server.common.data.id.CustomerId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.id.UserId;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.controller.BaseController;
-import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.dao.plugin.PluginService;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.extensions.api.plugins.PluginApiCallSecurityContext;
import org.thingsboard.server.extensions.api.plugins.PluginConstants;
-import org.thingsboard.server.extensions.api.plugins.rest.BasicPluginRestMsg;
-import org.thingsboard.server.extensions.api.plugins.rest.RestRequest;
-
-import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping(PluginConstants.PLUGIN_URL_PREFIX)
diff --git a/application/src/main/java/org/thingsboard/server/controller/PluginController.java b/application/src/main/java/org/thingsboard/server/controller/PluginController.java
index ed17600..3bc385d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/PluginController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/PluginController.java
@@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index f69ad81..f20ff8f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -28,20 +28,18 @@ import org.springframework.web.bind.annotation.*;
import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
-import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.script.NashornJsEngine;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleController.java b/application/src/main/java/org/thingsboard/server/controller/RuleController.java
index 9a26902..4528d81 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleController.java
@@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.data.security.Authority;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 6ff8287..80ddc2b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -58,7 +58,7 @@ import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.extensions.api.exception.InvalidParametersException;
import org.thingsboard.server.extensions.api.exception.UncheckedApiException;
import org.thingsboard.server.extensions.api.plugins.PluginConstants;
diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantController.java b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
index 5acb4eb..bf49074 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TenantController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TenantController.java
@@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.tenant.TenantService;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@RestController
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java
index 2a1531a..52b207d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/UserController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java
@@ -29,9 +29,9 @@ import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
-import org.thingsboard.server.service.mail.MailService;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.servlet.http.HttpServletRequest;
diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
index 757f765..bf89f13 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
@@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
index 44c7d94..43ece89 100644
--- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
@@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.dao.model.ModelConstants;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import java.util.List;
diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
index 3b897d6..031073f 100644
--- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
+++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.exception;
import org.springframework.http.HttpStatus;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import java.util.Date;
diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
index c70c561..63fe17a 100644
--- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
+++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
@@ -17,8 +17,6 @@ package org.thingsboard.server.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -27,6 +25,8 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException;
import org.thingsboard.server.service.security.exception.JwtExpiredTokenException;
diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
index 25b911c..818c935 100644
--- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
+++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
@@ -27,13 +27,15 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.ui.velocity.VelocityEngineUtils;
+import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings;
+import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.settings.AdminSettingsService;
-import org.thingsboard.server.exception.ThingsboardErrorCode;
-import org.thingsboard.server.exception.ThingsboardException;
import javax.annotation.PostConstruct;
+import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Locale;
@@ -49,18 +51,18 @@ public class DefaultMailService implements MailService {
public static final String UTF_8 = "UTF-8";
@Autowired
private MessageSource messages;
-
+
@Autowired
@Qualifier("velocityEngine")
private VelocityEngine engine;
-
+
private JavaMailSenderImpl mailSender;
-
+
private String mailFrom;
-
+
@Autowired
- private AdminSettingsService adminSettingsService;
-
+ private AdminSettingsService adminSettingsService;
+
@PostConstruct
private void init() {
updateMailConfiguration();
@@ -77,7 +79,7 @@ public class DefaultMailService implements MailService {
throw new IncorrectParameterException("Failed to date mail configuration. Settings not found!");
}
}
-
+
private JavaMailSenderImpl createMailSender(JsonNode jsonConfig) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(jsonConfig.get("smtpHost").asText());
@@ -99,7 +101,7 @@ public class DefaultMailService implements MailService {
javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false");
return javaMailProperties;
}
-
+
private int parsePort(String strPort) {
try {
return Integer.valueOf(strPort);
@@ -112,86 +114,102 @@ public class DefaultMailService implements MailService {
public void sendEmail(String email, String subject, String message) throws ThingsboardException {
sendMail(mailSender, mailFrom, email, subject, message);
}
-
+
@Override
public void sendTestMail(JsonNode jsonConfig, String email) throws ThingsboardException {
JavaMailSenderImpl testMailSender = createMailSender(jsonConfig);
String mailFrom = jsonConfig.get("mailFrom").asText();
String subject = messages.getMessage("test.message.subject", null, Locale.US);
-
+
Map<String, Object> model = new HashMap<String, Object>();
model.put(TARGET_EMAIL, email);
-
+
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"test.vm", UTF_8, model);
-
- sendMail(testMailSender, mailFrom, email, subject, message);
+
+ sendMail(testMailSender, mailFrom, email, subject, message);
}
@Override
public void sendActivationEmail(String activationLink, String email) throws ThingsboardException {
-
+
String subject = messages.getMessage("activation.subject", null, Locale.US);
-
+
Map<String, Object> model = new HashMap<String, Object>();
model.put("activationLink", activationLink);
model.put(TARGET_EMAIL, email);
-
+
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"activation.vm", UTF_8, model);
-
- sendMail(mailSender, mailFrom, email, subject, message);
+
+ sendMail(mailSender, mailFrom, email, subject, message);
}
-
+
@Override
public void sendAccountActivatedEmail(String loginLink, String email) throws ThingsboardException {
-
+
String subject = messages.getMessage("account.activated.subject", null, Locale.US);
-
+
Map<String, Object> model = new HashMap<String, Object>();
model.put("loginLink", loginLink);
model.put(TARGET_EMAIL, email);
-
+
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"account.activated.vm", UTF_8, model);
-
- sendMail(mailSender, mailFrom, email, subject, message);
+
+ sendMail(mailSender, mailFrom, email, subject, message);
}
@Override
public void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException {
-
+
String subject = messages.getMessage("reset.password.subject", null, Locale.US);
-
+
Map<String, Object> model = new HashMap<String, Object>();
model.put("passwordResetLink", passwordResetLink);
model.put(TARGET_EMAIL, email);
-
+
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"reset.password.vm", UTF_8, model);
-
- sendMail(mailSender, mailFrom, email, subject, message);
+
+ sendMail(mailSender, mailFrom, email, subject, message);
}
-
+
@Override
public void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException {
-
+
String subject = messages.getMessage("password.was.reset.subject", null, Locale.US);
-
+
Map<String, Object> model = new HashMap<String, Object>();
model.put("loginLink", loginLink);
model.put(TARGET_EMAIL, email);
-
+
String message = VelocityEngineUtils.mergeTemplateIntoString(this.engine,
"password.was.reset.vm", UTF_8, model);
-
- sendMail(mailSender, mailFrom, email, subject, message);
+
+ sendMail(mailSender, mailFrom, email, subject, message);
}
+ @Override
+ public void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
+ MimeMessage mailMsg = mailSender.createMimeMessage();
+ MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
+ helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from);
+ helper.setTo(to.split("\\s*,\\s*"));
+ if (!StringUtils.isBlank(cc)) {
+ helper.setCc(cc.split("\\s*,\\s*"));
+ }
+ if (!StringUtils.isBlank(bcc)) {
+ helper.setBcc(bcc.split("\\s*,\\s*"));
+ }
+ helper.setSubject(subject);
+ helper.setText(body);
+ mailSender.send(helper.getMimeMessage());
+ }
- private void sendMail(JavaMailSenderImpl mailSender,
- String mailFrom, String email,
- String subject, String message) throws ThingsboardException {
+ private void sendMail(JavaMailSenderImpl mailSender,
+ String mailFrom, String email,
+ String subject, String message) throws ThingsboardException {
try {
MimeMessage mimeMsg = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMsg, UTF_8);
@@ -208,7 +226,7 @@ public class DefaultMailService implements MailService {
protected ThingsboardException handleException(Exception exception) {
String message;
if (exception instanceof NestedRuntimeException) {
- message = ((NestedRuntimeException)exception).getMostSpecificCause().getMessage();
+ message = ((NestedRuntimeException) exception).getMostSpecificCause().getMessage();
} else {
message = exception.getMessage();
}
diff --git a/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java b/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java
new file mode 100644
index 0000000..97c8606
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.mail;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.thingsboard.rule.engine.api.ListeningExecutor;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+@Component
+public class MailExecutorService implements ListeningExecutor {
+
+ @Value("${actors.rule.mail_thread_pool_size}")
+ private int mailExecutorThreadPoolSize;
+
+ private ListeningExecutorService service;
+
+ @PostConstruct
+ public void init() {
+ this.service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(mailExecutorThreadPoolSize));
+ }
+
+ @PreDestroy
+ public void destroy() {
+ if (this.service != null) {
+ this.service.shutdown();
+ }
+ }
+
+ @Override
+ public <T> ListenableFuture<T> executeAsync(Callable<T> task) {
+ return service.submit(task);
+ }
+}
diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
index 6943940..c1f3688 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
+++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
@@ -45,7 +45,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.extensions.api.exception.ToErrorResponseEntity;
import org.thingsboard.server.service.security.model.SecurityUser;
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 4ffa998..b5011e2 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -221,6 +221,7 @@ actors:
error_persist_frequency: "${ACTORS_RULE_ERROR_FREQUENCY:3000}"
# Specify thread pool size for javascript executor service
js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:10}"
+ mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:10}"
chain:
# Errors for particular actor are persisted once per specified amount of milliseconds
error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}"
diff --git a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java b/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
index ed3750d..ba2bb65 100644
--- a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
+++ b/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
@@ -22,7 +22,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
-import org.thingsboard.server.exception.ThingsboardException;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
@Profile("test")
@Configuration
diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java
new file mode 100644
index 0000000..e6a48e2
--- /dev/null
+++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.service.script;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.google.common.collect.Sets;
+import org.junit.Test;
+import org.thingsboard.rule.engine.api.ScriptEngine;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
+
+import javax.script.ScriptException;
+
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+public class NashornJsEngineTest {
+
+ private ScriptEngine scriptEngine;
+
+ @Test
+ public void msgCanBeUpdated() throws ScriptException {
+ String function = "metadata.temp = metadata.temp * 10; return {metadata: metadata};";
+ scriptEngine = new NashornJsEngine(function, "Transform");
+
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "7");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+
+ TbMsg actual = scriptEngine.executeUpdate(msg);
+ assertEquals("70", actual.getMetaData().getValue("temp"));
+ }
+
+ @Test
+ public void newAttributesCanBeAddedInMsg() throws ScriptException {
+ String function = "metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};";
+ scriptEngine = new NashornJsEngine(function, "Transform");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "7");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+
+ TbMsg actual = scriptEngine.executeUpdate(msg);
+ assertEquals("94", actual.getMetaData().getValue("newAttr"));
+ }
+
+ @Test
+ public void payloadCanBeUpdated() throws ScriptException {
+ String function = "msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};";
+ scriptEngine = new NashornJsEngine(function, "Transform");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "7");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+
+ TbMsg actual = scriptEngine.executeUpdate(msg);
+
+ String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
+ assertEquals(expectedJson, actual.getData());
+ }
+
+ @Test
+ public void metadataAccessibleForFilter() throws ScriptException {
+ String function = "return metadata.humidity < 15;";
+ scriptEngine = new NashornJsEngine(function, "Filter");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "7");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ assertFalse(scriptEngine.executeFilter(msg));
+ }
+
+ @Test
+ public void dataAccessibleForFilter() throws ScriptException {
+ String function = "return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 7 && msg.bigObj.prop == 42;";
+ scriptEngine = new NashornJsEngine(function, "Filter");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "7");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ assertTrue(scriptEngine.executeFilter(msg));
+ }
+
+ @Test
+ public void dataAccessibleForSwitch() throws ScriptException {
+ String jsCode = "function nextRelation(metadata, msg) {\n" +
+ " if(msg.passed == 5 && metadata.temp == 10)\n" +
+ " return 'one'\n" +
+ " else\n" +
+ " return 'two';\n" +
+ "};\n" +
+ "\n" +
+ "return nextRelation(metadata, msg);";
+ scriptEngine = new NashornJsEngine(jsCode, "Switch");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "10");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ Set<String> actual = scriptEngine.executeSwitch(msg);
+ assertEquals(Sets.newHashSet("one"), actual);
+ }
+
+ @Test
+ public void multipleRelationsReturnedFromSwitch() throws ScriptException {
+ String jsCode = "function nextRelation(metadata, msg) {\n" +
+ " if(msg.passed == 5 && metadata.temp == 10)\n" +
+ " return ['three', 'one']\n" +
+ " else\n" +
+ " return 'two';\n" +
+ "};\n" +
+ "\n" +
+ "return nextRelation(metadata, msg);";
+ scriptEngine = new NashornJsEngine(jsCode, "Switch");
+ TbMsgMetaData metaData = new TbMsgMetaData();
+ metaData.putValue("temp", "10");
+ metaData.putValue("humidity", "99");
+ String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ Set<String> actual = scriptEngine.executeSwitch(msg);
+ assertEquals(Sets.newHashSet("one", "three"), actual);
+ }
+
+}
\ No newline at end of file
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java
index 7b016f5..15c52dc 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.common.data;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.CustomerId;
@@ -139,14 +140,17 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
return builder.toString();
}
+ @JsonIgnore
public boolean isSystemAdmin() {
return tenantId == null || EntityId.NULL_UUID.equals(tenantId.getId());
}
+ @JsonIgnore
public boolean isTenantAdmin() {
return !isSystemAdmin() && (customerId == null || EntityId.NULL_UUID.equals(customerId.getId()));
}
+ @JsonIgnore
public boolean isCustomerUser() {
return !isSystemAdmin() && !isTenantAdmin();
}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java
index ce13e23..4b7314c 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java
@@ -45,6 +45,10 @@ public final class TbMsgMetaData implements Serializable {
data.put(key, value);
}
+ public Map<String, String> values() {
+ return new HashMap<>(data);
+ }
+
public TbMsgMetaData copy() {
return new TbMsgMetaData(new ConcurrentHashMap<>(data));
}
diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
index 258b173..fcc9b5c 100644
--- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
+++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
@@ -86,6 +86,10 @@ public interface TbContext {
ListeningExecutor getJsExecutor();
+ ListeningExecutor getMailExecutor();
+
+ MailService getMailService();
+
ScriptEngine createJsScriptEngine(String script, String functionName, String... argNames);
}
rule-engine/rule-engine-components/pom.xml 10(+10 -0)
diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml
index 3deea9b..419b5f6 100644
--- a/rule-engine/rule-engine-components/pom.xml
+++ b/rule-engine/rule-engine-components/pom.xml
@@ -72,6 +72,16 @@
<artifactId>guava</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity-tools</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/EmailPojo.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/EmailPojo.java
new file mode 100644
index 0000000..35eaa3b
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/EmailPojo.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@Builder
+class EmailPojo {
+
+ private final String from;
+ private final String to;
+ private final String cc;
+ private final String bcc;
+ private final String subject;
+ private final String body;
+
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/RuleVelocityUtils.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/RuleVelocityUtils.java
new file mode 100644
index 0000000..7413cad
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/RuleVelocityUtils.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.runtime.parser.ParseException;
+import org.apache.velocity.runtime.parser.node.SimpleNode;
+import org.thingsboard.server.common.msg.TbMsg;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Map;
+
+import static org.thingsboard.server.common.msg.TbMsgDataType.JSON;
+
+public class RuleVelocityUtils {
+
+ public static VelocityContext createContext(TbMsg msg) throws IOException {
+ VelocityContext context = new VelocityContext();
+ context.put("originator", msg.getOriginator());
+ context.put("type", msg.getType());
+ context.put("metadata", msg.getMetaData().values());
+ if (msg.getDataType() == JSON) {
+ Map map = new ObjectMapper().readValue(msg.getData(), Map.class);
+ context.put("msg", map);
+ } else {
+ context.put("msg", msg.getData());
+ }
+ return context;
+ }
+
+ public static String merge(Template template, VelocityContext context) {
+ StringWriter writer = new StringWriter();
+ template.merge(context, writer);
+ return writer.toString();
+ }
+
+ public static Template create(String source, String templateName) throws ParseException {
+ RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
+ StringReader reader = new StringReader(source);
+ SimpleNode node = runtimeServices.parse(reader, templateName);
+ Template template = new Template();
+ template.setRuntimeServices(runtimeServices);
+ template.setData(node);
+ template.initDocument();
+ return template;
+ }
+
+
+}
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
new file mode 100644
index 0000000..a35e694
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
@@ -0,0 +1,114 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.runtime.parser.ParseException;
+import org.springframework.util.StringUtils;
+import org.thingsboard.rule.engine.TbNodeUtils;
+import org.thingsboard.rule.engine.api.*;
+import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static org.thingsboard.rule.engine.mail.TbSendEmailNode.SEND_EMAIL_TYPE;
+
+@Slf4j
+@RuleNode(
+ type = ComponentType.TRANSFORMATION,
+ name = "to email",
+ configClazz = TbMsgToEmailNodeConfiguration.class,
+ nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity",
+ 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. ")
+public class TbMsgToEmailNode implements TbNode {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private TbMsgToEmailNodeConfiguration config;
+
+ private Optional<Template> fromTemplate;
+ private Optional<Template> toTemplate;
+ private Optional<Template> ccTemplate;
+ private Optional<Template> bccTemplate;
+ private Optional<Template> subjectTemplate;
+ private Optional<Template> bodyTemplate;
+
+ @Override
+ public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
+ this.config = TbNodeUtils.convert(configuration, TbMsgToEmailNodeConfiguration.class);
+ try {
+ fromTemplate = toTemplate(config.getFromTemplate(), "From Template");
+ toTemplate = toTemplate(config.getToTemplate(), "To Template");
+ ccTemplate = toTemplate(config.getCcTemplate(), "Cc Template");
+ bccTemplate = toTemplate(config.getBccTemplate(), "Bcc Template");
+ subjectTemplate = toTemplate(config.getSubjectTemplate(), "Subject Template");
+ bodyTemplate = toTemplate(config.getBodyTemplate(), "Body Template");
+ } catch (ParseException e) {
+ log.error("Failed to create templates based on provided configuration!", e);
+ throw new TbNodeException(e);
+ }
+ }
+
+ @Override
+ public void onMsg(TbContext ctx, TbMsg msg) {
+ try {
+ EmailPojo email = convert(msg);
+ TbMsg emailMsg = buildEmailMsg(msg, email);
+ ctx.tellNext(emailMsg);
+ } catch (Exception ex) {
+ log.warn("Can not convert message to email " + ex.getMessage());
+ ctx.tellError(msg, ex);
+ }
+ }
+
+ private TbMsg buildEmailMsg(TbMsg msg, EmailPojo email) throws JsonProcessingException {
+ String emailJson = MAPPER.writeValueAsString(email);
+ return new TbMsg(UUIDs.timeBased(), SEND_EMAIL_TYPE, msg.getOriginator(), msg.getMetaData().copy(), emailJson);
+ }
+
+ private EmailPojo convert(TbMsg msg) throws IOException {
+ EmailPojo.EmailPojoBuilder builder = EmailPojo.builder();
+ VelocityContext context = RuleVelocityUtils.createContext(msg);
+ fromTemplate.ifPresent(t -> builder.from(RuleVelocityUtils.merge(t, context)));
+ toTemplate.ifPresent(t -> builder.to(RuleVelocityUtils.merge(t, context)));
+ ccTemplate.ifPresent(t -> builder.cc(RuleVelocityUtils.merge(t, context)));
+ bccTemplate.ifPresent(t -> builder.bcc(RuleVelocityUtils.merge(t, context)));
+ subjectTemplate.ifPresent(t -> builder.subject(RuleVelocityUtils.merge(t, context)));
+ bodyTemplate.ifPresent(t -> builder.body(RuleVelocityUtils.merge(t, context)));
+ return builder.build();
+ }
+
+ private Optional<Template> toTemplate(String source, String name) throws ParseException {
+ if (!StringUtils.isEmpty(source)) {
+ return Optional.of(RuleVelocityUtils.create(source, name));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
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
new file mode 100644
index 0000000..6b0aa58
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import lombok.Data;
+import org.thingsboard.rule.engine.api.NodeConfiguration;
+
+@Data
+public class TbMsgToEmailNodeConfiguration implements NodeConfiguration {
+
+ private String fromTemplate;
+ private String toTemplate;
+ private String ccTemplate;
+ private String bccTemplate;
+ private String subjectTemplate;
+ private String bodyTemplate;
+
+ @Override
+ public TbMsgToEmailNodeConfiguration defaultConfiguration() {
+ TbMsgToEmailNodeConfiguration configuration = new TbMsgToEmailNodeConfiguration();
+ configuration.fromTemplate = "info@testmail.org";
+ configuration.toTemplate = "$metadata.userEmail";
+ configuration.subjectTemplate = "Device $deviceType temperature high";
+ configuration.bodyTemplate = "Device $metadata.deviceName has high temperature $msg.temp";
+
+ return configuration;
+ }
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
new file mode 100644
index 0000000..407476b
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.thingsboard.rule.engine.TbNodeUtils;
+import org.thingsboard.rule.engine.api.*;
+import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.msg.TbMsg;
+
+import java.io.IOException;
+
+import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
+
+@Slf4j
+@RuleNode(
+ type = ComponentType.ACTION,
+ name = "send email",
+ configClazz = TbSendEmailNodeConfiguration.class,
+ nodeDescription = "Log incoming messages using JS script for transformation Message into String",
+ nodeDetails = "Transform incoming Message with configured JS condition to String and log final value. " +
+ "Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>" +
+ "Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>")
+public class TbSendEmailNode implements TbNode {
+
+ static final String SEND_EMAIL_TYPE = "SEND_EMAIL";
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private TbSendEmailNodeConfiguration config;
+
+ @Override
+ public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
+ this.config = TbNodeUtils.convert(configuration, TbSendEmailNodeConfiguration.class);
+ }
+
+ @Override
+ public void onMsg(TbContext ctx, TbMsg msg) {
+ try {
+ validateType(msg.getType());
+ EmailPojo email = getEmail(msg);
+ withCallback(ctx.getMailExecutor().executeAsync(() -> {
+ ctx.getMailService().send(email.getFrom(), email.getTo(), email.getCc(),
+ email.getBcc(), email.getSubject(), email.getBody());
+ return null;
+ }),
+ ok -> ctx.tellNext(msg),
+ fail -> ctx.tellError(msg, fail));
+ } catch (Exception ex) {
+ ctx.tellError(msg, ex);
+ }
+
+
+ }
+
+ private EmailPojo getEmail(TbMsg msg) throws IOException {
+ EmailPojo email = MAPPER.readValue(msg.getData(), EmailPojo.class);
+ if (StringUtils.isBlank(email.getTo())) {
+ throw new IllegalStateException("Email destination can not be blank [" + email.getTo() + "]");
+ }
+ return email;
+ }
+
+ private void validateType(String type) {
+ if (!SEND_EMAIL_TYPE.equals(type)) {
+ log.warn("Not expected msg type [{}] for SendEmail Node", type);
+ throw new IllegalStateException("Not expected msg type " + type + " for SendEmail Node");
+ }
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java
new file mode 100644
index 0000000..4768b7d
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import lombok.Data;
+import org.thingsboard.rule.engine.api.NodeConfiguration;
+
+@Data
+public class TbSendEmailNodeConfiguration implements NodeConfiguration {
+
+ private String tmp;
+
+ @Override
+ public TbSendEmailNodeConfiguration defaultConfiguration() {
+ TbSendEmailNodeConfiguration configuration = new TbSendEmailNodeConfiguration();
+ configuration.tmp = "";
+ return configuration;
+ }
+}
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
index fc65e43..84cff22 100644
--- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
@@ -42,7 +42,7 @@ import static org.thingsboard.server.common.data.DataConstants.*;
nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
"with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " +
- "<code>metadata.cs.temperature</code> or <code>metadata.shared.limit</code> " +
+ "<code>metadata.cs_temperature</code> or <code>metadata.shared_limit</code> " +
"If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeOriginatorAttributesConfig")
@@ -63,9 +63,9 @@ public class TbGetAttributesNode implements TbNode {
t -> ctx.tellError(msg, t));
} else {
ListenableFuture<List<Void>> future = Futures.allAsList(
- putAttrAsync(ctx, msg, CLIENT_SCOPE, config.getClientAttributeNames(), "cs."),
- putAttrAsync(ctx, msg, SHARED_SCOPE, config.getSharedAttributeNames(), "shared."),
- putAttrAsync(ctx, msg, SERVER_SCOPE, config.getServerAttributeNames(), "ss."));
+ putAttrAsync(ctx, msg, CLIENT_SCOPE, config.getClientAttributeNames(), "cs_"),
+ putAttrAsync(ctx, msg, SHARED_SCOPE, config.getSharedAttributeNames(), "shared_"),
+ putAttrAsync(ctx, msg, SERVER_SCOPE, config.getServerAttributeNames(), "ss_"));
withCallback(future, i -> ctx.tellNext(msg), t -> ctx.tellError(msg, t));
}
diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java
index 34232b2..08a22f0 100644
--- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java
+++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java
@@ -26,10 +26,7 @@ import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
-import org.thingsboard.rule.engine.api.ListeningExecutor;
-import org.thingsboard.rule.engine.api.TbContext;
-import org.thingsboard.rule.engine.api.TbNodeConfiguration;
-import org.thingsboard.rule.engine.api.TbNodeException;
+import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@@ -48,103 +45,57 @@ public class TbJsFilterNodeTest {
private TbContext ctx;
@Mock
private ListeningExecutor executor;
+ @Mock
+ private ScriptEngine scriptEngine;
@Test
- public void falseEvaluationDoNotSendMsg() throws TbNodeException {
- initWithScript("return 10 > 15;");
+ public void falseEvaluationDoNotSendMsg() throws TbNodeException, ScriptException {
+ initWithScript();
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}");
-
mockJsExecutor();
+ when(scriptEngine.executeFilter(msg)).thenReturn(false);
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
verify(ctx).tellNext(msg, "false");
- verifyNoMoreInteractions(ctx);
}
@Test
- public void notValidMsgDataThrowsException() throws TbNodeException {
- initWithScript("return 10 > 15;");
- TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, null, "{}");
-
- when(ctx.getJsExecutor()).thenReturn(executor);
-
- mockJsExecutor();
-
- node.onMsg(ctx, msg);
- verifyError(msg, "Cannot bind js args", IllegalArgumentException.class);
- }
-
- @Test
- public void exceptionInJsThrowsException() throws TbNodeException {
- initWithScript("return metadata.temp.curr < 15;");
+ public void exceptionInJsThrowsException() throws TbNodeException, ScriptException {
+ initWithScript();
TbMsgMetaData metaData = new TbMsgMetaData();
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
mockJsExecutor();
+ when(scriptEngine.executeFilter(msg)).thenThrow(new ScriptException("error"));
- node.onMsg(ctx, msg);
- String expectedMessage = "TypeError: Cannot read property \"curr\" from undefined in <eval> at line number 1";
- verifyError(msg, expectedMessage, ScriptException.class);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void notValidScriptThrowsException() throws TbNodeException {
- initWithScript("return 10 > 15 asdq out");
- }
-
- @Test
- public void metadataConditionCanBeFalse() throws TbNodeException {
- initWithScript("return metadata.humidity < 15;");
- TbMsgMetaData metaData = new TbMsgMetaData();
- metaData.putValue("temp", "10");
- metaData.putValue("humidity", "99");
- TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
- mockJsExecutor();
node.onMsg(ctx, msg);
- verify(ctx).getJsExecutor();
- verify(ctx).tellNext(msg, "false");
- verifyNoMoreInteractions(ctx);
+ verifyError(msg, "error", ScriptException.class);
}
@Test
- public void metadataConditionCanBeTrue() throws TbNodeException {
- initWithScript("return metadata.temp < 15;");
+ public void metadataConditionCanBeTrue() throws TbNodeException, ScriptException {
+ initWithScript();
TbMsgMetaData metaData = new TbMsgMetaData();
- metaData.putValue("temp", "10");
- metaData.putValue("humidity", "99");
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}");
mockJsExecutor();
+ when(scriptEngine.executeFilter(msg)).thenReturn(true);
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
verify(ctx).tellNext(msg, "true");
}
- @Test
- public void msgJsonParsedAndBinded() throws TbNodeException {
- initWithScript("return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 10 && msg.bigObj.prop == 42;");
- TbMsgMetaData metaData = new TbMsgMetaData();
- metaData.putValue("temp", "10");
- metaData.putValue("humidity", "99");
- String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
-
- TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
- mockJsExecutor();
-
- node.onMsg(ctx, msg);
- verify(ctx).getJsExecutor();
- verify(ctx).tellNext(msg, "true");
- }
-
- private void initWithScript(String script) throws TbNodeException {
+ private void initWithScript() throws TbNodeException {
TbJsFilterNodeConfiguration config = new TbJsFilterNodeConfiguration();
- config.setJsScript(script);
+ config.setJsScript("scr");
ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
+ when(ctx.createJsScriptEngine("scr", "Filter")).thenReturn(scriptEngine);
+
node = new TbJsFilterNode();
- node.init(null, nodeConfiguration);
+ node.init(ctx, nodeConfiguration);
}
private void mockJsExecutor() {
diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java
index a4ea56c..a495124 100644
--- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java
+++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java
@@ -27,14 +27,11 @@ import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
-import org.thingsboard.rule.engine.api.ListeningExecutor;
-import org.thingsboard.rule.engine.api.TbContext;
-import org.thingsboard.rule.engine.api.TbNodeConfiguration;
-import org.thingsboard.rule.engine.api.TbNodeException;
+import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
-import java.util.HashSet;
+import javax.script.ScriptException;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -51,18 +48,12 @@ public class TbJsSwitchNodeTest {
private TbContext ctx;
@Mock
private ListeningExecutor executor;
+ @Mock
+ private ScriptEngine scriptEngine;
@Test
- public void multipleRoutesAreAllowed() throws TbNodeException {
- String jsCode = "function nextRelation(metadata, msg) {\n" +
- " if(msg.passed == 5 && metadata.temp == 10)\n" +
- " return ['three', 'one']\n" +
- " else\n" +
- " return 'two';\n" +
- "};\n" +
- "\n" +
- "return nextRelation(metadata, msg);";
- initWithScript(jsCode);
+ public void multipleRoutesAreAllowed() throws TbNodeException, ScriptException {
+ initWithScript();
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
@@ -70,44 +61,23 @@ public class TbJsSwitchNodeTest {
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
mockJsExecutor();
+ when(scriptEngine.executeSwitch(msg)).thenReturn(Sets.newHashSet("one", "three"));
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
verify(ctx).tellNext(msg, Sets.newHashSet("one", "three"));
}
- @Test
- public void allowedRelationPassed() throws TbNodeException {
- String jsCode = "function nextRelation(metadata, msg) {\n" +
- " if(msg.passed == 5 && metadata.temp == 10)\n" +
- " return 'one'\n" +
- " else\n" +
- " return 'two';\n" +
- "};\n" +
- "\n" +
- "return nextRelation(metadata, msg);";
- initWithScript(jsCode);
- TbMsgMetaData metaData = new TbMsgMetaData();
- metaData.putValue("temp", "10");
- metaData.putValue("humidity", "99");
- String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
-
- TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
- mockJsExecutor();
-
- node.onMsg(ctx, msg);
- verify(ctx).getJsExecutor();
- verify(ctx).tellNext(msg, Sets.newHashSet("one"));
- }
-
- private void initWithScript(String script) throws TbNodeException {
+ private void initWithScript() throws TbNodeException {
TbJsSwitchNodeConfiguration config = new TbJsSwitchNodeConfiguration();
- config.setJsScript(script);
+ config.setJsScript("scr");
ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
+ when(ctx.createJsScriptEngine("scr", "Switch")).thenReturn(scriptEngine);
+
node = new TbJsSwitchNode();
- node.init(null, nodeConfiguration);
+ node.init(ctx, nodeConfiguration);
}
private void mockJsExecutor() {
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
new file mode 100644
index 0000000..877047c
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.rule.engine.mail;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.thingsboard.rule.engine.api.TbContext;
+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.msg.TbMsg;
+import org.thingsboard.server.common.msg.TbMsgMetaData;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TbMsgToEmailNodeTest {
+
+ private TbMsgToEmailNode emailNode;
+
+ @Mock
+ private TbContext ctx;
+
+ 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
+ public void msgCanBeConverted() throws IOException {
+ initWithScript();
+ metaData.putValue("username", "oreo");
+ metaData.putValue("userEmail", "user@email.io");
+ TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", originator, metaData, rawJson);
+
+ emailNode.onMsg(ctx, msg);
+
+ ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
+ verify(ctx).tellNext(captor.capture());
+ TbMsg actualMsg = captor.getValue();
+
+ assertEquals("SEND_EMAIL", actualMsg.getType());
+ assertEquals(originator, actualMsg.getOriginator());
+ assertEquals("oreo", actualMsg.getMetaData().getValue("username"));
+ assertNotSame(metaData, actualMsg.getMetaData());
+
+
+ EmailPojo actual = new ObjectMapper().readValue(actualMsg.getData().getBytes(), EmailPojo.class);
+
+ EmailPojo expected = new EmailPojo.EmailPojoBuilder()
+ .from("test@mail.org")
+ .to("user@email.io")
+ .subject("Hi oreo there")
+ .body("temp is to high. Current 5 and 100")
+ .build();
+ assertEquals(expected, actual);
+ }
+
+ private void initWithScript() {
+ try {
+ TbMsgToEmailNodeConfiguration config = new TbMsgToEmailNodeConfiguration();
+ config.setFromTemplate("test@mail.org");
+ config.setToTemplate("$metadata.userEmail");
+ config.setSubjectTemplate("Hi $metadata.username there");
+ config.setBodyTemplate("$msg.name is to high. Current $msg.passed and $msg.complex.count");
+ ObjectMapper mapper = new ObjectMapper();
+ TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
+
+ emailNode = new TbMsgToEmailNode();
+ emailNode.init(ctx, nodeConfiguration);
+ } catch (TbNodeException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java
index 579392c..b904d7e 100644
--- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java
+++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java
@@ -26,13 +26,11 @@ import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
-import org.thingsboard.rule.engine.api.ListeningExecutor;
-import org.thingsboard.rule.engine.api.TbContext;
-import org.thingsboard.rule.engine.api.TbNodeConfiguration;
-import org.thingsboard.rule.engine.api.TbNodeException;
+import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import javax.script.ScriptException;
import java.util.concurrent.Callable;
import static org.junit.Assert.assertEquals;
@@ -48,73 +46,76 @@ public class TbTransformMsgNodeTest {
private TbContext ctx;
@Mock
private ListeningExecutor executor;
+ @Mock
+ private ScriptEngine scriptEngine;
@Test
- public void metadataCanBeUpdated() throws TbNodeException {
- initWithScript("metadata.temp = metadata.temp * 10; return {metadata: metadata};");
+ public void metadataCanBeUpdated() throws TbNodeException, ScriptException {
+ initWithScript(false);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
- metaData.putValue("humidity", "99");
- String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+ String rawJson = "{\"passed\": 5}";
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ TbMsg transformedMsg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{new}");
mockJsExecutor();
+ when(scriptEngine.executeUpdate(msg)).thenReturn(transformedMsg);
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx).tellNext(captor.capture());
TbMsg actualMsg = captor.getValue();
- assertEquals("70", actualMsg.getMetaData().getValue("temp"));
+ assertEquals(transformedMsg, actualMsg);
}
+
@Test
- public void metadataCanBeAdded() throws TbNodeException {
- initWithScript("metadata.newAttr = metadata.humidity - msg.passed; return {metadata: metadata};");
+ public void newChainCanBeStarted() throws TbNodeException, ScriptException {
+ initWithScript(true);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
- metaData.putValue("humidity", "99");
- String rawJson = "{\"name\": \"Vit\", \"passed\": 5, \"bigObj\": {\"prop\":42}}";
+ String rawJson = "{\"passed\": 5";
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
+ TbMsg transformedMsg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{new}");
mockJsExecutor();
+ when(scriptEngine.executeUpdate(msg)).thenReturn(transformedMsg);
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
- verify(ctx).tellNext(captor.capture());
+ verify(ctx).spawn(captor.capture());
TbMsg actualMsg = captor.getValue();
- assertEquals("94", actualMsg.getMetaData().getValue("newAttr"));
+ assertEquals(transformedMsg, actualMsg);
}
@Test
- public void payloadCanBeUpdated() throws TbNodeException {
- initWithScript("msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine'; return {msg: msg};");
+ public void exceptionHandledCorrectly() throws TbNodeException, ScriptException {
+ initWithScript(false);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
- metaData.putValue("humidity", "99");
- String rawJson = "{\"name\":\"Vit\",\"passed\": 5,\"bigObj\":{\"prop\":42}}";
+ String rawJson = "{\"passed\": 5";
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson);
mockJsExecutor();
+ when(scriptEngine.executeUpdate(msg)).thenThrow(new IllegalStateException("error"));
node.onMsg(ctx, msg);
- verify(ctx).getJsExecutor();
- ArgumentCaptor<TbMsg> captor = ArgumentCaptor.forClass(TbMsg.class);
- verify(ctx).tellNext(captor.capture());
- TbMsg actualMsg = captor.getValue();
- String expectedJson = "{\"name\":\"Vit\",\"passed\":35,\"bigObj\":{\"prop\":42,\"newProp\":\"Ukraine\"}}";
- assertEquals(expectedJson, new String(actualMsg.getData()));
+ verifyError(msg, "error", IllegalStateException.class);
}
- private void initWithScript(String script) throws TbNodeException {
+ private void initWithScript(boolean startChain) throws TbNodeException {
TbTransformMsgNodeConfiguration config = new TbTransformMsgNodeConfiguration();
- config.setJsScript(script);
+ config.setJsScript("scr");
+ config.setStartNewChain(startChain);
ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));
+ when(ctx.createJsScriptEngine("scr", "Transform")).thenReturn(scriptEngine);
+
node = new TbTransformMsgNode();
- node.init(null, nodeConfiguration);
+ node.init(ctx, nodeConfiguration);
}
private void mockJsExecutor() {