Browse Source

Merge branch 'develop/1.5' of github.com:thingsboard/thingsboard into develop/1.5

pull/725/head
Igor Kulikov 8 years ago
parent
commit
fbfac3e6b2
  1. 13
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  2. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  3. 2
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  4. 4
      application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java
  5. 4
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  6. 4
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  7. 4
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  8. 2
      application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
  9. 6
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  10. 8
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  11. 2
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  12. 2
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  13. 4
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  14. 5
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  15. 4
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  16. 4
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  17. 2
      application/src/main/java/org/thingsboard/server/controller/PluginController.java
  18. 4
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  19. 2
      application/src/main/java/org/thingsboard/server/controller/RuleController.java
  20. 2
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  21. 2
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  22. 6
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  23. 2
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  24. 2
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  25. 21
      application/src/main/java/org/thingsboard/server/controller/plugin/PluginApiController.java
  26. 1
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java
  27. 4
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  28. 100
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  29. 54
      application/src/main/java/org/thingsboard/server/service/mail/MailExecutorService.java
  30. 2
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  31. 1
      application/src/main/resources/thingsboard.yml
  32. 3
      application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
  33. 151
      application/src/test/java/org/thingsboard/server/service/script/NashornJsEngineTest.java
  34. 4
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  35. 2
      common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java
  36. 2
      common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java
  37. 4
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgMetaData.java
  38. 10
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java
  39. 4
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
  40. 10
      rule-engine/rule-engine-components/pom.xml
  41. 32
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/EmailPojo.java
  42. 68
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/RuleVelocityUtils.java
  43. 114
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
  44. 41
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java
  45. 89
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
  46. 32
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java
  47. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
  48. 85
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java
  49. 54
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java
  50. 98
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java
  51. 57
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java

13
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

15
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;
@ -135,6 +132,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getJsExecutor();
}
@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();
}
}

2
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;

4
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;

4
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;

4
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")

4
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;

2
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;

6
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;

8
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;

2
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;

2
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")

4
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;

5
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;

4
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;

4
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")

2
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;

4
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;

2
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;

2
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;

2
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")

6
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;

2
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;

2
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;

21
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)

1
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;

4
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;

100
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();
}

54
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);
}
}

2
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;

1
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}"

3
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

151
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);
}
}

4
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();
}

2
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorCode.java → common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.exception;
package org.thingsboard.server.common.data.exception;
import com.fasterxml.jackson.annotation.JsonValue;

2
application/src/main/java/org/thingsboard/server/exception/ThingsboardException.java → common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardException.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.exception;
package org.thingsboard.server.common.data.exception;
public class ThingsboardException extends Exception {

4
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));
}

10
application/src/main/java/org/thingsboard/server/service/mail/MailService.java → rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/MailService.java

@ -13,12 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.mail;
package org.thingsboard.rule.engine.api;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import com.fasterxml.jackson.databind.JsonNode;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
public interface MailService {
void updateMailConfiguration();
@ -34,5 +37,6 @@ public interface MailService {
void sendResetPasswordEmail(String passwordResetLink, String email) throws ThingsboardException;
void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException;
void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
}

4
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);
}

10
rule-engine/rule-engine-components/pom.xml

@ -71,6 +71,16 @@
<groupId>com.google.guava</groupId>
<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>

32
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;
}

68
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;
}
}

114
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() {
}
}

41
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;
}
}

89
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() {
}
}

32
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;
}
}

8
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));
}

85
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() {

54
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() {

98
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);
}
}
}

57
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() {

Loading…
Cancel
Save