Commit 8808d66c9d19a5d3f073019c1f7cc07780c1274b

Authored by Artem Halushko
2 parents 0ee3f77d 56fb09b9

Merge branch 'master' of https://github.com/ArtemHalushko/thingsboard into trip-select

Showing 60 changed files with 987 additions and 744 deletions

Too many changes to show.

To preserve performance only 60 of 724 files are displayed.

@@ -153,12 +153,8 @@ @@ -153,12 +153,8 @@
153 <artifactId>jjwt</artifactId> 153 <artifactId>jjwt</artifactId>
154 </dependency> 154 </dependency>
155 <dependency> 155 <dependency>
156 - <groupId>org.apache.velocity</groupId>  
157 - <artifactId>velocity</artifactId>  
158 - </dependency>  
159 - <dependency>  
160 - <groupId>org.apache.velocity</groupId>  
161 - <artifactId>velocity-tools</artifactId> 156 + <groupId>org.freemarker</groupId>
  157 + <artifactId>freemarker</artifactId>
162 </dependency> 158 </dependency>
163 <dependency> 159 <dependency>
164 <groupId>commons-io</groupId> 160 <groupId>commons-io</groupId>
@@ -130,7 +130,7 @@ @@ -130,7 +130,7 @@
130 "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n \n self.getSettingsSchema = function() {\n return TbTripAnimationWidget.getSettingsSchema();\n}\n", 130 "controllerScript": " self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n }\n \n \n self.actionSources = function () {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n };\n \n self.getSettingsSchema = function() {\n return TbTripAnimationWidget.getSettingsSchema();\n}\n",
131 "settingsSchema": "", 131 "settingsSchema": "",
132 "dataKeySettingsSchema": "{}", 132 "dataKeySettingsSchema": "{}",
133 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : value + 2)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : value + 2)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><b>End Time:</b> ${maxTime}<br/><b>Start Time:</b> ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"polKeyName\":\"coordinates\",\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}" 133 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><b>End Time:</b> ${maxTime}<br/><b>Start Time:</b> ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"polKeyName\":\"coordinates\",\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"<b>${entityName}</b><br/><br/><b>TimeStamp:</b> ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}"
134 } 134 }
135 }, 135 },
136 { 136 {
@@ -150,4 +150,4 @@ @@ -150,4 +150,4 @@
150 } 150 }
151 } 151 }
152 ] 152 ]
153 -}  
  153 +}
@@ -56,6 +56,7 @@ import org.thingsboard.server.dao.audit.AuditLogService; @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.audit.AuditLogService;
56 import org.thingsboard.server.dao.cassandra.CassandraCluster; 56 import org.thingsboard.server.dao.cassandra.CassandraCluster;
57 import org.thingsboard.server.dao.customer.CustomerService; 57 import org.thingsboard.server.dao.customer.CustomerService;
58 import org.thingsboard.server.dao.dashboard.DashboardService; 58 import org.thingsboard.server.dao.dashboard.DashboardService;
  59 +import org.thingsboard.server.dao.device.ClaimDevicesService;
59 import org.thingsboard.server.dao.device.DeviceService; 60 import org.thingsboard.server.dao.device.DeviceService;
60 import org.thingsboard.server.dao.entityview.EntityViewService; 61 import org.thingsboard.server.dao.entityview.EntityViewService;
61 import org.thingsboard.server.dao.event.EventService; 62 import org.thingsboard.server.dao.event.EventService;
@@ -218,6 +219,10 @@ public class ActorSystemContext { @@ -218,6 +219,10 @@ public class ActorSystemContext {
218 @Getter 219 @Getter
219 private MailService mailService; 220 private MailService mailService;
220 221
  222 + @Autowired
  223 + @Getter
  224 + private ClaimDevicesService claimDevicesService;
  225 +
221 //TODO: separate context for TbCore and TbRuleEngine 226 //TODO: separate context for TbCore and TbRuleEngine
222 @Autowired(required = false) 227 @Autowired(required = false)
223 @Getter 228 @Getter
@@ -39,6 +39,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -39,6 +39,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
39 import org.thingsboard.server.common.msg.queue.TbCallback; 39 import org.thingsboard.server.common.msg.queue.TbCallback;
40 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 40 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
41 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 41 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  42 +import org.thingsboard.server.gen.transport.TransportProtos;
42 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 43 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
43 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 44 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
44 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 45 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
@@ -232,9 +233,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -232,9 +233,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
232 if (msg.hasSubscriptionInfo()) { 233 if (msg.hasSubscriptionInfo()) {
233 handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); 234 handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
234 } 235 }
  236 + if (msg.hasClaimDevice()) {
  237 + handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice());
  238 + }
235 callback.onSuccess(); 239 callback.onSuccess();
236 } 240 }
237 241
  242 + private void handleClaimDeviceMsg(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) {
  243 + DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
  244 + systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
  245 + }
  246 +
238 private void reportSessionOpen() { 247 private void reportSessionOpen() {
239 systemContext.getDeviceStateService().onDeviceConnect(deviceId); 248 systemContext.getDeviceStateService().onDeviceConnect(deviceId);
240 } 249 }
@@ -15,28 +15,11 @@ @@ -15,28 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.config; 16 package org.thingsboard.server.config;
17 17
18 -import lombok.extern.slf4j.Slf4j;  
19 -import org.apache.commons.collections.ExtendedProperties;  
20 -import org.apache.commons.logging.Log;  
21 -import org.apache.commons.logging.LogFactory;  
22 -import org.apache.velocity.app.VelocityEngine;  
23 -import org.apache.velocity.exception.ResourceNotFoundException;  
24 -import org.apache.velocity.runtime.RuntimeConstants;  
25 -import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;  
26 import org.springframework.context.MessageSource; 18 import org.springframework.context.MessageSource;
27 import org.springframework.context.annotation.Bean; 19 import org.springframework.context.annotation.Bean;
28 import org.springframework.context.annotation.Configuration; 20 import org.springframework.context.annotation.Configuration;
29 import org.springframework.context.annotation.Primary; 21 import org.springframework.context.annotation.Primary;
30 import org.springframework.context.support.ResourceBundleMessageSource; 22 import org.springframework.context.support.ResourceBundleMessageSource;
31 -import org.springframework.core.io.DefaultResourceLoader;  
32 -import org.springframework.core.io.Resource;  
33 -import org.springframework.core.io.ResourceLoader;  
34 -import org.springframework.util.StringUtils;  
35 -  
36 -import java.io.File;  
37 -import java.io.IOException;  
38 -import java.io.InputStream;  
39 -import java.util.Arrays;  
40 23
41 @Configuration 24 @Configuration
42 public class ThingsboardMessageConfiguration { 25 public class ThingsboardMessageConfiguration {
@@ -49,114 +32,4 @@ public class ThingsboardMessageConfiguration { @@ -49,114 +32,4 @@ public class ThingsboardMessageConfiguration {
49 messageSource.setDefaultEncoding("UTF-8"); 32 messageSource.setDefaultEncoding("UTF-8");
50 return messageSource; 33 return messageSource;
51 } 34 }
52 -  
53 - private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:/templates/";  
54 -  
55 - private ResourceLoader resourceLoader = new DefaultResourceLoader();  
56 -  
57 - @Bean  
58 - public VelocityEngine velocityEngine() {  
59 - VelocityEngine velocityEngine = new VelocityEngine();  
60 - try {  
61 - Resource resource = resourceLoader.getResource(DEFAULT_RESOURCE_LOADER_PATH);  
62 - File file = resource.getFile();  
63 - velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");  
64 - velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");  
65 - velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, file.getAbsolutePath());  
66 - } catch (IOException e) {  
67 - initSpringResourceLoader(velocityEngine, DEFAULT_RESOURCE_LOADER_PATH);  
68 - }  
69 - velocityEngine.init();  
70 - return velocityEngine;  
71 - }  
72 -  
73 - private void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) {  
74 - velocityEngine.setProperty(  
75 - RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME);  
76 - velocityEngine.setProperty(  
77 - SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName());  
78 - velocityEngine.setProperty(  
79 - SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true");  
80 - velocityEngine.setApplicationAttribute(  
81 - SpringResourceLoader.SPRING_RESOURCE_LOADER, resourceLoader);  
82 - velocityEngine.setApplicationAttribute(  
83 - SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath);  
84 - }  
85 -  
86 - @Slf4j  
87 - public static class SpringResourceLoader extends org.apache.velocity.runtime.resource.loader.ResourceLoader {  
88 -  
89 - public static final String NAME = "spring";  
90 -  
91 - public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class";  
92 -  
93 - public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache";  
94 -  
95 - public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader";  
96 -  
97 - public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path";  
98 -  
99 - private org.springframework.core.io.ResourceLoader resourceLoader;  
100 -  
101 - private String[] resourceLoaderPaths;  
102 -  
103 -  
104 - @Override  
105 - public void init(ExtendedProperties configuration) {  
106 - this.resourceLoader = (org.springframework.core.io.ResourceLoader)  
107 - this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER);  
108 - String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH);  
109 - if (this.resourceLoader == null) {  
110 - throw new IllegalArgumentException(  
111 - "'resourceLoader' application attribute must be present for SpringResourceLoader");  
112 - }  
113 - if (resourceLoaderPath == null) {  
114 - throw new IllegalArgumentException(  
115 - "'resourceLoaderPath' application attribute must be present for SpringResourceLoader");  
116 - }  
117 - this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);  
118 - for (int i = 0; i < this.resourceLoaderPaths.length; i++) {  
119 - String path = this.resourceLoaderPaths[i];  
120 - if (!path.endsWith("/")) {  
121 - this.resourceLoaderPaths[i] = path + "/";  
122 - }  
123 - }  
124 - if (log.isInfoEnabled()) {  
125 - log.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader +  
126 - "] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths));  
127 - }  
128 - }  
129 -  
130 - @Override  
131 - public InputStream getResourceStream(String source) throws ResourceNotFoundException {  
132 - if (log.isDebugEnabled()) {  
133 - log.debug("Looking for Velocity resource with name [" + source + "]");  
134 - }  
135 - for (String resourceLoaderPath : this.resourceLoaderPaths) {  
136 - org.springframework.core.io.Resource resource =  
137 - this.resourceLoader.getResource(resourceLoaderPath + source);  
138 - try {  
139 - return resource.getInputStream();  
140 - }  
141 - catch (IOException ex) {  
142 - if (log.isDebugEnabled()) {  
143 - log.debug("Could not find Velocity resource: " + resource);  
144 - }  
145 - }  
146 - }  
147 - throw new ResourceNotFoundException(  
148 - "Could not find resource [" + source + "] in Spring resource loader path");  
149 - }  
150 -  
151 - @Override  
152 - public boolean isSourceModified(org.apache.velocity.runtime.resource.Resource resource) {  
153 - return false;  
154 - }  
155 -  
156 - @Override  
157 - public long getLastModified(org.apache.velocity.runtime.resource.Resource resource) {  
158 - return 0;  
159 - }  
160 -  
161 - }  
162 } 35 }
@@ -135,7 +135,7 @@ public class UserController extends BaseController { @@ -135,7 +135,7 @@ public class UserController extends BaseController {
135 HttpServletRequest request) throws ThingsboardException { 135 HttpServletRequest request) throws ThingsboardException {
136 try { 136 try {
137 137
138 - if (getCurrentUser().getAuthority() == Authority.TENANT_ADMIN) { 138 + if (Authority.TENANT_ADMIN.equals(getCurrentUser().getAuthority())) {
139 user.setTenantId(getCurrentUser().getTenantId()); 139 user.setTenantId(getCurrentUser().getTenantId());
140 } 140 }
141 141
@@ -60,7 +60,7 @@ public class WidgetTypeController extends BaseController { @@ -60,7 +60,7 @@ public class WidgetTypeController extends BaseController {
60 @ResponseBody 60 @ResponseBody
61 public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException { 61 public WidgetType saveWidgetType(@RequestBody WidgetType widgetType) throws ThingsboardException {
62 try { 62 try {
63 - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { 63 + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
64 widgetType.setTenantId(TenantId.SYS_TENANT_ID); 64 widgetType.setTenantId(TenantId.SYS_TENANT_ID);
65 } else { 65 } else {
66 widgetType.setTenantId(getCurrentUser().getTenantId()); 66 widgetType.setTenantId(getCurrentUser().getTenantId());
@@ -61,7 +61,7 @@ public class WidgetsBundleController extends BaseController { @@ -61,7 +61,7 @@ public class WidgetsBundleController extends BaseController {
61 @ResponseBody 61 @ResponseBody
62 public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException { 62 public WidgetsBundle saveWidgetsBundle(@RequestBody WidgetsBundle widgetsBundle) throws ThingsboardException {
63 try { 63 try {
64 - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { 64 + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
65 widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); 65 widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID);
66 } else { 66 } else {
67 widgetsBundle.setTenantId(getCurrentUser().getTenantId()); 67 widgetsBundle.setTenantId(getCurrentUser().getTenantId());
@@ -103,7 +103,7 @@ public class WidgetsBundleController extends BaseController { @@ -103,7 +103,7 @@ public class WidgetsBundleController extends BaseController {
103 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 103 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
104 try { 104 try {
105 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 105 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
106 - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { 106 + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
107 return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); 107 return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink));
108 } else { 108 } else {
109 TenantId tenantId = getCurrentUser().getTenantId(); 109 TenantId tenantId = getCurrentUser().getTenantId();
@@ -119,7 +119,7 @@ public class WidgetsBundleController extends BaseController { @@ -119,7 +119,7 @@ public class WidgetsBundleController extends BaseController {
119 @ResponseBody 119 @ResponseBody
120 public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException { 120 public List<WidgetsBundle> getWidgetsBundles() throws ThingsboardException {
121 try { 121 try {
122 - if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) { 122 + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
123 return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId())); 123 return checkNotNull(widgetsBundleService.findSystemWidgetsBundles(getTenantId()));
124 } else { 124 } else {
125 TenantId tenantId = getCurrentUser().getTenantId(); 125 TenantId tenantId = getCurrentUser().getTenantId();
@@ -131,6 +131,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -131,6 +131,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
131 node.put("username", ""); 131 node.put("username", "");
132 node.put("password", ""); 132 node.put("password", "");
133 node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself) 133 node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself)
  134 + node.put("enableProxy", false);
134 mailSettings.setJsonValue(node); 135 mailSettings.setJsonValue(node);
135 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings); 136 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
136 } 137 }
@@ -16,18 +16,17 @@ @@ -16,18 +16,17 @@
16 package org.thingsboard.server.service.mail; 16 package org.thingsboard.server.service.mail;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
  19 +import freemarker.template.Configuration;
  20 +import freemarker.template.Template;
19 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
20 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
21 -import org.apache.velocity.VelocityContext;  
22 -import org.apache.velocity.app.VelocityEngine;  
23 -import org.apache.velocity.exception.VelocityException;  
24 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
25 -import org.springframework.beans.factory.annotation.Qualifier;  
26 import org.springframework.context.MessageSource; 24 import org.springframework.context.MessageSource;
27 import org.springframework.core.NestedRuntimeException; 25 import org.springframework.core.NestedRuntimeException;
28 import org.springframework.mail.javamail.JavaMailSenderImpl; 26 import org.springframework.mail.javamail.JavaMailSenderImpl;
29 import org.springframework.mail.javamail.MimeMessageHelper; 27 import org.springframework.mail.javamail.MimeMessageHelper;
30 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
  29 +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
31 import org.thingsboard.rule.engine.api.MailService; 30 import org.thingsboard.rule.engine.api.MailService;
32 import org.thingsboard.server.common.data.AdminSettings; 31 import org.thingsboard.server.common.data.AdminSettings;
33 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 32 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
@@ -40,8 +39,6 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; @@ -40,8 +39,6 @@ import org.thingsboard.server.dao.settings.AdminSettingsService;
40 import javax.annotation.PostConstruct; 39 import javax.annotation.PostConstruct;
41 import javax.mail.MessagingException; 40 import javax.mail.MessagingException;
42 import javax.mail.internet.MimeMessage; 41 import javax.mail.internet.MimeMessage;
43 -import java.io.StringWriter;  
44 -import java.io.Writer;  
45 import java.util.HashMap; 42 import java.util.HashMap;
46 import java.util.Locale; 43 import java.util.Locale;
47 import java.util.Map; 44 import java.util.Map;
@@ -58,8 +55,7 @@ public class DefaultMailService implements MailService { @@ -58,8 +55,7 @@ public class DefaultMailService implements MailService {
58 private MessageSource messages; 55 private MessageSource messages;
59 56
60 @Autowired 57 @Autowired
61 - @Qualifier("velocityEngine")  
62 - private VelocityEngine engine; 58 + private Configuration freemarkerConfig;
63 59
64 private JavaMailSenderImpl mailSender; 60 private JavaMailSenderImpl mailSender;
65 61
@@ -118,6 +114,21 @@ public class DefaultMailService implements MailService { @@ -118,6 +114,21 @@ public class DefaultMailService implements MailService {
118 javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", tlsVersion); 114 javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", tlsVersion);
119 } 115 }
120 } 116 }
  117 +
  118 + boolean enableProxy = jsonConfig.has("enableProxy") && jsonConfig.get("enableProxy").asBoolean();
  119 +
  120 + if (enableProxy) {
  121 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.host", jsonConfig.get("proxyHost").asText());
  122 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.port", jsonConfig.get("proxyPort").asText());
  123 + String proxyUser = jsonConfig.get("proxyUser").asText();
  124 + if (StringUtils.isNoneEmpty(proxyUser)) {
  125 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.user", proxyUser);
  126 + }
  127 + String proxyPassword = jsonConfig.get("proxyPassword").asText();
  128 + if (StringUtils.isNoneEmpty(proxyPassword)) {
  129 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.password", proxyPassword);
  130 + }
  131 + }
121 return javaMailProperties; 132 return javaMailProperties;
122 } 133 }
123 134
@@ -140,11 +151,10 @@ public class DefaultMailService implements MailService { @@ -140,11 +151,10 @@ public class DefaultMailService implements MailService {
140 String mailFrom = jsonConfig.get("mailFrom").asText(); 151 String mailFrom = jsonConfig.get("mailFrom").asText();
141 String subject = messages.getMessage("test.message.subject", null, Locale.US); 152 String subject = messages.getMessage("test.message.subject", null, Locale.US);
142 153
143 - Map<String, Object> model = new HashMap<String, Object>(); 154 + Map<String, Object> model = new HashMap<>();
144 model.put(TARGET_EMAIL, email); 155 model.put(TARGET_EMAIL, email);
145 156
146 - String message = mergeTemplateIntoString(this.engine,  
147 - "test.vm", UTF_8, model); 157 + String message = mergeTemplateIntoString("test.ftl", model);
148 158
149 sendMail(testMailSender, mailFrom, email, subject, message); 159 sendMail(testMailSender, mailFrom, email, subject, message);
150 } 160 }
@@ -154,12 +164,11 @@ public class DefaultMailService implements MailService { @@ -154,12 +164,11 @@ public class DefaultMailService implements MailService {
154 164
155 String subject = messages.getMessage("activation.subject", null, Locale.US); 165 String subject = messages.getMessage("activation.subject", null, Locale.US);
156 166
157 - Map<String, Object> model = new HashMap<String, Object>(); 167 + Map<String, Object> model = new HashMap<>();
158 model.put("activationLink", activationLink); 168 model.put("activationLink", activationLink);
159 model.put(TARGET_EMAIL, email); 169 model.put(TARGET_EMAIL, email);
160 170
161 - String message = mergeTemplateIntoString(this.engine,  
162 - "activation.vm", UTF_8, model); 171 + String message = mergeTemplateIntoString("activation.ftl", model);
163 172
164 sendMail(mailSender, mailFrom, email, subject, message); 173 sendMail(mailSender, mailFrom, email, subject, message);
165 } 174 }
@@ -169,12 +178,11 @@ public class DefaultMailService implements MailService { @@ -169,12 +178,11 @@ public class DefaultMailService implements MailService {
169 178
170 String subject = messages.getMessage("account.activated.subject", null, Locale.US); 179 String subject = messages.getMessage("account.activated.subject", null, Locale.US);
171 180
172 - Map<String, Object> model = new HashMap<String, Object>(); 181 + Map<String, Object> model = new HashMap<>();
173 model.put("loginLink", loginLink); 182 model.put("loginLink", loginLink);
174 model.put(TARGET_EMAIL, email); 183 model.put(TARGET_EMAIL, email);
175 184
176 - String message = mergeTemplateIntoString(this.engine,  
177 - "account.activated.vm", UTF_8, model); 185 + String message = mergeTemplateIntoString("account.activated.ftl", model);
178 186
179 sendMail(mailSender, mailFrom, email, subject, message); 187 sendMail(mailSender, mailFrom, email, subject, message);
180 } 188 }
@@ -184,12 +192,11 @@ public class DefaultMailService implements MailService { @@ -184,12 +192,11 @@ public class DefaultMailService implements MailService {
184 192
185 String subject = messages.getMessage("reset.password.subject", null, Locale.US); 193 String subject = messages.getMessage("reset.password.subject", null, Locale.US);
186 194
187 - Map<String, Object> model = new HashMap<String, Object>(); 195 + Map<String, Object> model = new HashMap<>();
188 model.put("passwordResetLink", passwordResetLink); 196 model.put("passwordResetLink", passwordResetLink);
189 model.put(TARGET_EMAIL, email); 197 model.put(TARGET_EMAIL, email);
190 198
191 - String message = mergeTemplateIntoString(this.engine,  
192 - "reset.password.vm", UTF_8, model); 199 + String message = mergeTemplateIntoString("reset.password.ftl", model);
193 200
194 sendMail(mailSender, mailFrom, email, subject, message); 201 sendMail(mailSender, mailFrom, email, subject, message);
195 } 202 }
@@ -199,12 +206,11 @@ public class DefaultMailService implements MailService { @@ -199,12 +206,11 @@ public class DefaultMailService implements MailService {
199 206
200 String subject = messages.getMessage("password.was.reset.subject", null, Locale.US); 207 String subject = messages.getMessage("password.was.reset.subject", null, Locale.US);
201 208
202 - Map<String, Object> model = new HashMap<String, Object>(); 209 + Map<String, Object> model = new HashMap<>();
203 model.put("loginLink", loginLink); 210 model.put("loginLink", loginLink);
204 model.put(TARGET_EMAIL, email); 211 model.put(TARGET_EMAIL, email);
205 212
206 - String message = mergeTemplateIntoString(this.engine,  
207 - "password.was.reset.vm", UTF_8, model); 213 + String message = mergeTemplateIntoString("password.was.reset.ftl", model);
208 214
209 sendMail(mailSender, mailFrom, email, subject, message); 215 sendMail(mailSender, mailFrom, email, subject, message);
210 } 216 }
@@ -230,13 +236,12 @@ public class DefaultMailService implements MailService { @@ -230,13 +236,12 @@ public class DefaultMailService implements MailService {
230 public void sendAccountLockoutEmail(String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException { 236 public void sendAccountLockoutEmail(String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException {
231 String subject = messages.getMessage("account.lockout.subject", null, Locale.US); 237 String subject = messages.getMessage("account.lockout.subject", null, Locale.US);
232 238
233 - Map<String, Object> model = new HashMap<String, Object>(); 239 + Map<String, Object> model = new HashMap<>();
234 model.put("lockoutAccount", lockoutEmail); 240 model.put("lockoutAccount", lockoutEmail);
235 model.put("maxFailedLoginAttempts", maxFailedLoginAttempts); 241 model.put("maxFailedLoginAttempts", maxFailedLoginAttempts);
236 model.put(TARGET_EMAIL, email); 242 model.put(TARGET_EMAIL, email);
237 243
238 - String message = mergeTemplateIntoString(this.engine,  
239 - "account.lockout.vm", UTF_8, model); 244 + String message = mergeTemplateIntoString("account.lockout.ftl", model);
240 245
241 sendMail(mailSender, mailFrom, email, subject, message); 246 sendMail(mailSender, mailFrom, email, subject, message);
242 } 247 }
@@ -257,20 +262,14 @@ public class DefaultMailService implements MailService { @@ -257,20 +262,14 @@ public class DefaultMailService implements MailService {
257 } 262 }
258 } 263 }
259 264
260 - private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation,  
261 - String encoding, Map<String, Object> model) throws VelocityException {  
262 -  
263 - StringWriter result = new StringWriter();  
264 - mergeTemplate(velocityEngine, templateLocation, encoding, model, result);  
265 - return result.toString();  
266 - }  
267 -  
268 - private static void mergeTemplate(  
269 - VelocityEngine velocityEngine, String templateLocation, String encoding,  
270 - Map<String, Object> model, Writer writer) throws VelocityException {  
271 -  
272 - VelocityContext velocityContext = new VelocityContext(model);  
273 - velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer); 265 + private String mergeTemplateIntoString(String templateLocation,
  266 + Map<String, Object> model) throws ThingsboardException {
  267 + try {
  268 + Template template = freemarkerConfig.getTemplate(templateLocation);
  269 + return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
  270 + } catch (Exception e) {
  271 + throw handleException(e);
  272 + }
274 } 273 }
275 274
276 protected ThingsboardException handleException(Exception exception) { 275 protected ThingsboardException handleException(Exception exception) {
@@ -46,6 +46,9 @@ import java.util.concurrent.atomic.AtomicInteger; @@ -46,6 +46,9 @@ import java.util.concurrent.atomic.AtomicInteger;
46 @Service 46 @Service
47 public class RemoteJsInvokeService extends AbstractJsInvokeService { 47 public class RemoteJsInvokeService extends AbstractJsInvokeService {
48 48
  49 + @Value("${queue.js.max_eval_requests_timeout}")
  50 + private long maxEvalRequestsTimeout;
  51 +
49 @Value("${queue.js.max_requests_timeout}") 52 @Value("${queue.js.max_requests_timeout}")
50 private long maxRequestsTimeout; 53 private long maxRequestsTimeout;
51 54
@@ -59,22 +62,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -59,22 +62,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
59 @Value("${js.remote.stats.enabled:false}") 62 @Value("${js.remote.stats.enabled:false}")
60 private boolean statsEnabled; 63 private boolean statsEnabled;
61 64
62 - private final AtomicInteger kafkaPushedMsgs = new AtomicInteger(0);  
63 - private final AtomicInteger kafkaInvokeMsgs = new AtomicInteger(0);  
64 - private final AtomicInteger kafkaEvalMsgs = new AtomicInteger(0);  
65 - private final AtomicInteger kafkaFailedMsgs = new AtomicInteger(0);  
66 - private final AtomicInteger kafkaTimeoutMsgs = new AtomicInteger(0); 65 + private final AtomicInteger queuePushedMsgs = new AtomicInteger(0);
  66 + private final AtomicInteger queueInvokeMsgs = new AtomicInteger(0);
  67 + private final AtomicInteger queueEvalMsgs = new AtomicInteger(0);
  68 + private final AtomicInteger queueFailedMsgs = new AtomicInteger(0);
  69 + private final AtomicInteger queueTimeoutMsgs = new AtomicInteger(0);
67 70
68 @Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}") 71 @Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms}")
69 public void printStats() { 72 public void printStats() {
70 if (statsEnabled) { 73 if (statsEnabled) {
71 - int pushedMsgs = kafkaPushedMsgs.getAndSet(0);  
72 - int invokeMsgs = kafkaInvokeMsgs.getAndSet(0);  
73 - int evalMsgs = kafkaEvalMsgs.getAndSet(0);  
74 - int failed = kafkaFailedMsgs.getAndSet(0);  
75 - int timedOut = kafkaTimeoutMsgs.getAndSet(0); 74 + int pushedMsgs = queuePushedMsgs.getAndSet(0);
  75 + int invokeMsgs = queueInvokeMsgs.getAndSet(0);
  76 + int evalMsgs = queueEvalMsgs.getAndSet(0);
  77 + int failed = queueFailedMsgs.getAndSet(0);
  78 + int timedOut = queueTimeoutMsgs.getAndSet(0);
76 if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) { 79 if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) {
77 - log.info("Kafka JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]", 80 + log.info("Queue JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
78 pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut); 81 pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut);
79 } 82 }
80 } 83 }
@@ -113,22 +116,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -113,22 +116,22 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
113 116
114 log.trace("Post compile request for scriptId [{}]", scriptId); 117 log.trace("Post compile request for scriptId [{}]", scriptId);
115 ListenableFuture<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper)); 118 ListenableFuture<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> future = requestTemplate.send(new TbProtoJsQueueMsg<>(UUID.randomUUID(), jsRequestWrapper));
116 - if (maxRequestsTimeout > 0) {  
117 - future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService); 119 + if (maxEvalRequestsTimeout > 0) {
  120 + future = Futures.withTimeout(future, maxEvalRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
118 } 121 }
119 - kafkaPushedMsgs.incrementAndGet(); 122 + queuePushedMsgs.incrementAndGet();
120 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() { 123 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() {
121 @Override 124 @Override
122 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) { 125 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) {
123 - kafkaEvalMsgs.incrementAndGet(); 126 + queueEvalMsgs.incrementAndGet();
124 } 127 }
125 128
126 @Override 129 @Override
127 public void onFailure(Throwable t) { 130 public void onFailure(Throwable t) {
128 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { 131 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
129 - kafkaTimeoutMsgs.incrementAndGet(); 132 + queueTimeoutMsgs.incrementAndGet();
130 } 133 }
131 - kafkaFailedMsgs.incrementAndGet(); 134 + queueFailedMsgs.incrementAndGet();
132 } 135 }
133 }, MoreExecutors.directExecutor()); 136 }, MoreExecutors.directExecutor());
134 return Futures.transform(future, response -> { 137 return Futures.transform(future, response -> {
@@ -170,20 +173,20 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService { @@ -170,20 +173,20 @@ public class RemoteJsInvokeService extends AbstractJsInvokeService {
170 if (maxRequestsTimeout > 0) { 173 if (maxRequestsTimeout > 0) {
171 future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService); 174 future = Futures.withTimeout(future, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
172 } 175 }
173 - kafkaPushedMsgs.incrementAndGet(); 176 + queuePushedMsgs.incrementAndGet();
174 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() { 177 Futures.addCallback(future, new FutureCallback<TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>>() {
175 @Override 178 @Override
176 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) { 179 public void onSuccess(@Nullable TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse> result) {
177 - kafkaInvokeMsgs.incrementAndGet(); 180 + queueInvokeMsgs.incrementAndGet();
178 } 181 }
179 182
180 @Override 183 @Override
181 public void onFailure(Throwable t) { 184 public void onFailure(Throwable t) {
182 onScriptExecutionError(scriptId); 185 onScriptExecutionError(scriptId);
183 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) { 186 if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
184 - kafkaTimeoutMsgs.incrementAndGet(); 187 + queueTimeoutMsgs.incrementAndGet();
185 } 188 }
186 - kafkaFailedMsgs.incrementAndGet(); 189 + queueFailedMsgs.incrementAndGet();
187 } 190 }
188 }, MoreExecutors.directExecutor()); 191 }, MoreExecutors.directExecutor());
189 return Futures.transform(future, response -> { 192 return Futures.transform(future, response -> {
@@ -105,7 +105,7 @@ public class CustomerUserPermissions extends AbstractPermissions { @@ -105,7 +105,7 @@ public class CustomerUserPermissions extends AbstractPermissions {
105 105
106 @Override 106 @Override
107 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) { 107 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
108 - if (userEntity.getAuthority() != Authority.CUSTOMER_USER) { 108 + if (!Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) {
109 return false; 109 return false;
110 } 110 }
111 if (!user.getId().equals(userId)) { 111 if (!user.getId().equals(userId)) {
@@ -57,7 +57,7 @@ public class SysAdminPermissions extends AbstractPermissions { @@ -57,7 +57,7 @@ public class SysAdminPermissions extends AbstractPermissions {
57 57
58 @Override 58 @Override
59 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) { 59 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
60 - if (userEntity.getAuthority() == Authority.CUSTOMER_USER) { 60 + if (Authority.CUSTOMER_USER.equals(userEntity.getAuthority())) {
61 return false; 61 return false;
62 } 62 }
63 return true; 63 return true;
@@ -76,7 +76,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -76,7 +76,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
76 76
77 @Override 77 @Override
78 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) { 78 public boolean hasPermission(SecurityUser user, Operation operation, UserId userId, User userEntity) {
79 - if (userEntity.getAuthority() == Authority.SYS_ADMIN) { 79 + if (Authority.SYS_ADMIN.equals(userEntity.getAuthority())) {
80 return false; 80 return false;
81 } 81 }
82 if (!user.getTenantId().equals(userEntity.getTenantId())) { 82 if (!user.getTenantId().equals(userEntity.getTenantId())) {
@@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer @@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
224 return null; 224 return null;
225 } 225 }
226 }, 226 },
227 - s -> (StringUtils.isEmpty(s.getScope()) || scope.equals(s.getScope().name())), 227 + s -> (TbAttributeSubscriptionScope.ANY_SCOPE.equals(s.getScope()) || scope.equals(s.getScope().name())),
228 s -> { 228 s -> {
229 List<TsKvEntry> subscriptionUpdate = null; 229 List<TsKvEntry> subscriptionUpdate = null;
230 for (AttributeKvEntry kv : attributes) { 230 for (AttributeKvEntry kv : attributes) {
@@ -17,6 +17,6 @@ package org.thingsboard.server.service.subscription; @@ -17,6 +17,6 @@ package org.thingsboard.server.service.subscription;
17 17
18 public enum TbAttributeSubscriptionScope { 18 public enum TbAttributeSubscriptionScope {
19 19
20 - CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE 20 + ANY_SCOPE, CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE
21 21
22 } 22 }
@@ -345,7 +345,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -345,7 +345,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
345 keys.forEach(key -> subState.put(key, 0L)); 345 keys.forEach(key -> subState.put(key, 0L));
346 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); 346 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
347 347
348 - TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope()); 348 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
349 349
350 TbAttributeSubscription sub = TbAttributeSubscription.builder() 350 TbAttributeSubscription sub = TbAttributeSubscription.builder()
351 .serviceId(serviceId) 351 .serviceId(serviceId)
@@ -442,7 +442,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -442,7 +442,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
442 Map<String, Long> subState = new HashMap<>(attributesData.size()); 442 Map<String, Long> subState = new HashMap<>(attributesData.size());
443 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs())); 443 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
444 444
445 - TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.SERVER_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope()); 445 + TbAttributeSubscriptionScope scope = StringUtils.isEmpty(cmd.getScope()) ? TbAttributeSubscriptionScope.ANY_SCOPE : TbAttributeSubscriptionScope.valueOf(cmd.getScope());
446 446
447 TbAttributeSubscription sub = TbAttributeSubscription.builder() 447 TbAttributeSubscription sub = TbAttributeSubscription.builder()
448 .serviceId(serviceId) 448 .serviceId(serviceId)
application/src/main/resources/templates/account.activated.ftl renamed from application/src/main/resources/templates/account.activated.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
@@ -96,7 +98,7 @@ background-color: #f6f6f6; @@ -96,7 +98,7 @@ background-color: #f6f6f6;
96 </tr> 98 </tr>
97 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 99 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
98 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 100 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
99 - <a href="$loginLink" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Login</a> 101 + <a href="${loginLink}" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Login</a>
100 </td> 102 </td>
101 </tr> 103 </tr>
102 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 104 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -109,7 +111,7 @@ background-color: #f6f6f6; @@ -109,7 +111,7 @@ background-color: #f6f6f6;
109 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 111 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
110 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 112 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
111 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 113 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
112 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 114 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
113 </tr> 115 </tr>
114 </table> 116 </table>
115 </div> 117 </div>
application/src/main/resources/templates/account.lockout.ftl renamed from application/src/main/resources/templates/test.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
19 <meta name="viewport" content="width=device-width" /> 21 <meta name="viewport" content="width=device-width" />
20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 22 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
21 -<title>Thingsboard - Test Message</title> 23 +<title>Thingsboard - Account Lockout</title>
22 24
23 25
24 <style type="text/css"> 26 <style type="text/css">
@@ -81,12 +83,12 @@ background-color: #f6f6f6; @@ -81,12 +83,12 @@ background-color: #f6f6f6;
81 <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 83 <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
82 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 84 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
83 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 85 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
84 - <h2>Test message from Thingsboard</h2> 86 + <h2>Thingsboard user account has been locked out</h2>
85 </td> 87 </td>
86 </tr> 88 </tr>
87 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 89 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
88 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 90 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
89 - This email is indicating that your outgoing mail settings were set up correctly. 91 + Thingsboard user account ${lockoutAccount} has been lockout due to failed credentials were provided more than ${maxFailedLoginAttempts} times.
90 </td> 92 </td>
91 </tr> 93 </tr>
92 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 94 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -99,7 +101,7 @@ background-color: #f6f6f6; @@ -99,7 +101,7 @@ background-color: #f6f6f6;
99 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 101 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
100 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 102 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
101 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 103 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
102 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 104 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
103 </tr> 105 </tr>
104 </table> 106 </table>
105 </div> 107 </div>
application/src/main/resources/templates/activation.ftl renamed from application/src/main/resources/templates/activation.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
@@ -96,7 +98,7 @@ background-color: #f6f6f6; @@ -96,7 +98,7 @@ background-color: #f6f6f6;
96 </tr> 98 </tr>
97 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 99 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
98 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 100 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
99 - <a href="$activationLink" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Activate your account</a> 101 + <a href="${activationLink}" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Activate your account</a>
100 </td> 102 </td>
101 </tr> 103 </tr>
102 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 104 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -109,7 +111,7 @@ background-color: #f6f6f6; @@ -109,7 +111,7 @@ background-color: #f6f6f6;
109 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 111 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
110 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 112 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
111 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 113 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
112 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 114 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
113 </tr> 115 </tr>
114 </table> 116 </table>
115 </div> 117 </div>
application/src/main/resources/templates/password.was.reset.ftl renamed from application/src/main/resources/templates/password.was.reset.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
@@ -96,7 +98,7 @@ background-color: #f6f6f6; @@ -96,7 +98,7 @@ background-color: #f6f6f6;
96 </tr> 98 </tr>
97 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 99 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
98 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 100 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
99 - <a href="$loginLink" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Login</a> 101 + <a href="${loginLink}" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Login</a>
100 </td> 102 </td>
101 </tr> 103 </tr>
102 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 104 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -109,7 +111,7 @@ background-color: #f6f6f6; @@ -109,7 +111,7 @@ background-color: #f6f6f6;
109 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 111 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
110 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 112 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
111 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 113 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
112 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 114 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
113 </tr> 115 </tr>
114 </table> 116 </table>
115 </div> 117 </div>
application/src/main/resources/templates/reset.password.ftl renamed from application/src/main/resources/templates/reset.password.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
@@ -96,7 +98,7 @@ background-color: #f6f6f6; @@ -96,7 +98,7 @@ background-color: #f6f6f6;
96 </tr> 98 </tr>
97 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 99 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
98 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 100 <td class="content-block" itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
99 - <a href="$passwordResetLink" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Reset password</a> 101 + <a href="${passwordResetLink}" class="btn-primary" itemprop="url" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;">Reset password</a>
100 </td> 102 </td>
101 </tr> 103 </tr>
102 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 104 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -109,7 +111,7 @@ background-color: #f6f6f6; @@ -109,7 +111,7 @@ background-color: #f6f6f6;
109 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 111 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
110 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 112 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
111 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 113 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
112 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 114 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
113 </tr> 115 </tr>
114 </table> 116 </table>
115 </div> 117 </div>
application/src/main/resources/templates/test.ftl renamed from application/src/main/resources/templates/account.lockout.vm
1 -#*  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - *# 1 +<#--
  2 +
  3 + Copyright © 2016-2020 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
16 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 18 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
17 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 19 <html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
18 <head> 20 <head>
19 <meta name="viewport" content="width=device-width" /> 21 <meta name="viewport" content="width=device-width" />
20 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 22 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
21 -<title>Thingsboard - Account Lockout</title> 23 +<title>Thingsboard - Test Message</title>
22 24
23 25
24 <style type="text/css"> 26 <style type="text/css">
@@ -81,12 +83,12 @@ background-color: #f6f6f6; @@ -81,12 +83,12 @@ background-color: #f6f6f6;
81 <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 83 <meta itemprop="name" content="Confirm Email" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;" /><table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
82 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 84 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
83 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 85 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
84 - <h2>Thingsboard user account has been locked out</h2> 86 + <h2>Test message from Thingsboard</h2>
85 </td> 87 </td>
86 </tr> 88 </tr>
87 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 89 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
88 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> 90 <td class="content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
89 - Thingsboard user account $lockoutAccount has been lockout due to failed credentials were provided more than $maxFailedLoginAttempts times. 91 + This email is indicating that your outgoing mail settings were set up correctly.
90 </td> 92 </td>
91 </tr> 93 </tr>
92 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 94 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
@@ -99,7 +101,7 @@ background-color: #f6f6f6; @@ -99,7 +101,7 @@ background-color: #f6f6f6;
99 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;"> 101 <div class="footer" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;">
100 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 102 <table width="100%" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
101 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> 103 <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;">
102 - <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:$targetEmail" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">$targetEmail</a> by Thingsboard.</td> 104 + <td class="aligncenter content-block" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top">This email was sent to <a href="mailto:${targetEmail}" style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;">${targetEmail}</a> by Thingsboard.</td>
103 </tr> 105 </tr>
104 </table> 106 </table>
105 </div> 107 </div>
@@ -668,6 +668,8 @@ queue: @@ -668,6 +668,8 @@ queue:
668 # JS Eval max pending requests 668 # JS Eval max pending requests
669 max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}" 669 max_pending_requests: "${REMOTE_JS_MAX_PENDING_REQUESTS:10000}"
670 # JS Eval max request timeout 670 # JS Eval max request timeout
  671 + max_eval_requests_timeout: "${REMOTE_JS_MAX_EVAL_REQUEST_TIMEOUT:60000}"
  672 + # JS max request timeout
671 max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}" 673 max_requests_timeout: "${REMOTE_JS_MAX_REQUEST_TIMEOUT:10000}"
672 # JS response poll interval 674 # JS response poll interval
673 response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" 675 response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}"
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.cassandra.guava;
  17 +
  18 +import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
  19 +import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
  20 +import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
  21 +import com.datastax.oss.driver.api.core.cql.ResultSet;
  22 +import com.datastax.oss.driver.api.core.cql.Row;
  23 +import com.datastax.oss.driver.api.core.cql.Statement;
  24 +import com.datastax.oss.driver.internal.core.util.CountingIterator;
  25 +import com.datastax.oss.driver.internal.core.util.concurrent.BlockingOperation;
  26 +import edu.umd.cs.findbugs.annotations.NonNull;
  27 +
  28 +import java.nio.ByteBuffer;
  29 +import java.util.ArrayList;
  30 +import java.util.Iterator;
  31 +import java.util.List;
  32 +
  33 +public class GuavaMultiPageResultSet implements ResultSet {
  34 +
  35 + private final RowIterator iterator;
  36 + private final List<ExecutionInfo> executionInfos = new ArrayList<>();
  37 + private ColumnDefinitions columnDefinitions;
  38 +
  39 + public GuavaMultiPageResultSet(@NonNull GuavaSession session, @NonNull Statement statement, @NonNull AsyncResultSet firstPage) {
  40 + assert firstPage.hasMorePages();
  41 + this.iterator = new RowIterator(session, statement, firstPage);
  42 + this.executionInfos.add(firstPage.getExecutionInfo());
  43 + this.columnDefinitions = firstPage.getColumnDefinitions();
  44 + }
  45 +
  46 + @NonNull
  47 + @Override
  48 + public ColumnDefinitions getColumnDefinitions() {
  49 + return columnDefinitions;
  50 + }
  51 +
  52 + @NonNull
  53 + @Override
  54 + public List<ExecutionInfo> getExecutionInfos() {
  55 + return executionInfos;
  56 + }
  57 +
  58 + @Override
  59 + public boolean isFullyFetched() {
  60 + return iterator.isFullyFetched();
  61 + }
  62 +
  63 + @Override
  64 + public int getAvailableWithoutFetching() {
  65 + return iterator.remaining();
  66 + }
  67 +
  68 + @NonNull
  69 + @Override
  70 + public Iterator<Row> iterator() {
  71 + return iterator;
  72 + }
  73 +
  74 + @Override
  75 + public boolean wasApplied() {
  76 + return iterator.wasApplied();
  77 + }
  78 +
  79 + private class RowIterator extends CountingIterator<Row> {
  80 + private GuavaSession session;
  81 + private Statement statement;
  82 + private AsyncResultSet currentPage;
  83 + private Iterator<Row> currentRows;
  84 +
  85 + private RowIterator(GuavaSession session, Statement statement, AsyncResultSet firstPage) {
  86 + super(firstPage.remaining());
  87 + this.session = session;
  88 + this.statement = statement;
  89 + this.currentPage = firstPage;
  90 + this.currentRows = firstPage.currentPage().iterator();
  91 + }
  92 +
  93 + @Override
  94 + protected Row computeNext() {
  95 + maybeMoveToNextPage();
  96 + return currentRows.hasNext() ? currentRows.next() : endOfData();
  97 + }
  98 +
  99 + private void maybeMoveToNextPage() {
  100 + if (!currentRows.hasNext() && currentPage.hasMorePages()) {
  101 + BlockingOperation.checkNotDriverThread();
  102 + ByteBuffer nextPagingState = currentPage.getExecutionInfo().getPagingState();
  103 + this.statement = this.statement.setPagingState(nextPagingState);
  104 + AsyncResultSet nextPage = GuavaSession.getSafe(this.session.executeAsync(this.statement));
  105 + currentPage = nextPage;
  106 + remaining += nextPage.remaining();
  107 + currentRows = nextPage.currentPage().iterator();
  108 + executionInfos.add(nextPage.getExecutionInfo());
  109 + // The definitions can change from page to page if this result set was built from a bound
  110 + // 'SELECT *', and the schema was altered.
  111 + columnDefinitions = nextPage.getColumnDefinitions();
  112 + }
  113 + }
  114 +
  115 + private boolean isFullyFetched() {
  116 + return !currentPage.hasMorePages();
  117 + }
  118 +
  119 + private boolean wasApplied() {
  120 + return currentPage.wasApplied();
  121 + }
  122 + }
  123 +}
@@ -17,13 +17,18 @@ package org.thingsboard.server.dao.cassandra.guava; @@ -17,13 +17,18 @@ package org.thingsboard.server.dao.cassandra.guava;
17 17
18 import com.datastax.oss.driver.api.core.cql.AsyncResultSet; 18 import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
19 import com.datastax.oss.driver.api.core.cql.PreparedStatement; 19 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
  20 +import com.datastax.oss.driver.api.core.cql.ResultSet;
20 import com.datastax.oss.driver.api.core.cql.SimpleStatement; 21 import com.datastax.oss.driver.api.core.cql.SimpleStatement;
21 import com.datastax.oss.driver.api.core.cql.Statement; 22 import com.datastax.oss.driver.api.core.cql.Statement;
22 import com.datastax.oss.driver.api.core.cql.SyncCqlSession; 23 import com.datastax.oss.driver.api.core.cql.SyncCqlSession;
23 import com.datastax.oss.driver.api.core.session.Session; 24 import com.datastax.oss.driver.api.core.session.Session;
24 import com.datastax.oss.driver.api.core.type.reflect.GenericType; 25 import com.datastax.oss.driver.api.core.type.reflect.GenericType;
25 import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest; 26 import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest;
  27 +import com.datastax.oss.driver.internal.core.cql.SinglePageResultSet;
26 import com.google.common.util.concurrent.ListenableFuture; 28 import com.google.common.util.concurrent.ListenableFuture;
  29 +import edu.umd.cs.findbugs.annotations.NonNull;
  30 +
  31 +import java.util.concurrent.ExecutionException;
27 32
28 public interface GuavaSession extends Session, SyncCqlSession { 33 public interface GuavaSession extends Session, SyncCqlSession {
29 34
@@ -33,6 +38,16 @@ public interface GuavaSession extends Session, SyncCqlSession { @@ -33,6 +38,16 @@ public interface GuavaSession extends Session, SyncCqlSession {
33 GenericType<ListenableFuture<PreparedStatement>> ASYNC_PREPARED = 38 GenericType<ListenableFuture<PreparedStatement>> ASYNC_PREPARED =
34 new GenericType<ListenableFuture<PreparedStatement>>() {}; 39 new GenericType<ListenableFuture<PreparedStatement>>() {};
35 40
  41 + @NonNull
  42 + default ResultSet execute(@NonNull Statement<?> statement) {
  43 + AsyncResultSet firstPage = getSafe(this.executeAsync(statement));
  44 + if (firstPage.hasMorePages()) {
  45 + return new GuavaMultiPageResultSet(this, statement, firstPage);
  46 + } else {
  47 + return new SinglePageResultSet(firstPage);
  48 + }
  49 + }
  50 +
36 default ListenableFuture<AsyncResultSet> executeAsync(Statement<?> statement) { 51 default ListenableFuture<AsyncResultSet> executeAsync(Statement<?> statement) {
37 return this.execute(statement, ASYNC); 52 return this.execute(statement, ASYNC);
38 } 53 }
@@ -48,4 +63,12 @@ public interface GuavaSession extends Session, SyncCqlSession { @@ -48,4 +63,12 @@ public interface GuavaSession extends Session, SyncCqlSession {
48 default ListenableFuture<PreparedStatement> prepareAsync(String statement) { 63 default ListenableFuture<PreparedStatement> prepareAsync(String statement) {
49 return this.prepareAsync(SimpleStatement.newInstance(statement)); 64 return this.prepareAsync(SimpleStatement.newInstance(statement));
50 } 65 }
  66 +
  67 + static AsyncResultSet getSafe(ListenableFuture<AsyncResultSet> future) {
  68 + try {
  69 + return future.get();
  70 + } catch (InterruptedException | ExecutionException e) {
  71 + throw new IllegalStateException(e);
  72 + }
  73 + }
51 } 74 }
@@ -19,10 +19,10 @@ version: '2.2' @@ -19,10 +19,10 @@ version: '2.2'
19 services: 19 services:
20 tb-js-executor: 20 tb-js-executor:
21 env_file: 21 env_file:
22 - - queue-pubsub.env.env 22 + - queue-pubsub.env
23 tb-core1: 23 tb-core1:
24 env_file: 24 env_file:
25 - - queue-pubsub.env.env 25 + - queue-pubsub.env
26 depends_on: 26 depends_on:
27 - zookeeper 27 - zookeeper
28 - redis 28 - redis
1 TB_QUEUE_TYPE=pubsub 1 TB_QUEUE_TYPE=pubsub
2 TB_QUEUE_PUBSUB_PROJECT_ID=YOUR_PROJECT_ID 2 TB_QUEUE_PUBSUB_PROJECT_ID=YOUR_PROJECT_ID
3 -TB_QUEUE_PUBSUB_SERVICE_ACCOUNT=YOUR_SERVICE_ACCOUNT  
  3 +TB_QUEUE_PUBSUB_SERVICE_ACCOUNT=YOUR_SERVICE_ACCOUNT
1 TB_QUEUE_TYPE=service-bus 1 TB_QUEUE_TYPE=service-bus
2 TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME=YOUR_NAMESPACE_NAME 2 TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME=YOUR_NAMESPACE_NAME
3 TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME=YOUR_SAS_KEY_NAME 3 TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME=YOUR_SAS_KEY_NAME
4 -TB_QUEUE_SERVICE_BUS_SAS_KEY=YOUR_SAS_KEY  
  4 +TB_QUEUE_SERVICE_BUS_SAS_KEY=YOUR_SAS_KEY
@@ -13,9 +13,9 @@ @@ -13,9 +13,9 @@
13 } 13 }
14 }, 14 },
15 "@azure/amqp-common": { 15 "@azure/amqp-common": {
16 - "version": "1.0.0-preview.13",  
17 - "resolved": "https://registry.npmjs.org/@azure/amqp-common/-/amqp-common-1.0.0-preview.13.tgz",  
18 - "integrity": "sha512-v19NGXFm8Hzr2bj/DSWYc2anaDcoAeFQXJGuBT8QO7eS13vaELQNGaynOGipEcI313A1778R/FFCk4o+dylIiw==", 16 + "version": "1.0.0-preview.15",
  17 + "resolved": "https://registry.npmjs.org/@azure/amqp-common/-/amqp-common-1.0.0-preview.15.tgz",
  18 + "integrity": "sha512-EoxNsVR7yLioNKRz5JBwQAE9pEdPVGCmmQbPKkZHP72vE5NhaLnOwHOCrk/311cuhJ8aQ60eiLUtF9J2XrEZyA==",
19 "requires": { 19 "requires": {
20 "@types/async-lock": "^1.1.0", 20 "@types/async-lock": "^1.1.0",
21 "@types/is-buffer": "^2.0.0", 21 "@types/is-buffer": "^2.0.0",
@@ -53,9 +53,9 @@ @@ -53,9 +53,9 @@
53 } 53 }
54 }, 54 },
55 "@azure/core-http": { 55 "@azure/core-http": {
56 - "version": "1.1.1",  
57 - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.1.tgz",  
58 - "integrity": "sha512-yBxH5CtYaCj0f1CKoi3OjQw5C5Go8TbgNA6Q2rX7XsDpN2eeKu0n3kRvzZnKW+brtO1u3YnBBuBLF2KcGoZv6g==", 56 + "version": "1.1.2",
  57 + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.2.tgz",
  58 + "integrity": "sha512-xeZpTs6caBIrRipqZs70jgrA+mAFxII5XrBzbOCELPs18n4QWfchB20F94ITAk3GuFVDaSBsOhVL3GP1J+ncGg==",
59 "requires": { 59 "requires": {
60 "@azure/abort-controller": "^1.0.0", 60 "@azure/abort-controller": "^1.0.0",
61 "@azure/core-auth": "^1.1.2", 61 "@azure/core-auth": "^1.1.2",
@@ -116,11 +116,11 @@ @@ -116,11 +116,11 @@
116 } 116 }
117 }, 117 },
118 "@azure/service-bus": { 118 "@azure/service-bus": {
119 - "version": "1.1.6",  
120 - "resolved": "https://registry.npmjs.org/@azure/service-bus/-/service-bus-1.1.6.tgz",  
121 - "integrity": "sha512-eCJXcJZGWdlVwLEqMcoIqtUrh/NtyFcDDfq/y8gdCOy3Dzuv8JkPTxjdjcxDthwG9mc5Qter3dGOTwh0U8gwiw==", 119 + "version": "1.1.7",
  120 + "resolved": "https://registry.npmjs.org/@azure/service-bus/-/service-bus-1.1.7.tgz",
  121 + "integrity": "sha512-wns3egBrP6UyT9CIPkM66KsOVJwit7VJT0P/t8PPPfUaO6yx3bEeZyVDq6WMiibnbIkgHtW85xXml4WDb+nPMw==",
122 "requires": { 122 "requires": {
123 - "@azure/amqp-common": "1.0.0-preview.13", 123 + "@azure/amqp-common": "1.0.0-preview.15",
124 "@azure/core-http": "^1.0.0", 124 "@azure/core-http": "^1.0.0",
125 "@opentelemetry/types": "^0.2.0", 125 "@opentelemetry/types": "^0.2.0",
126 "@types/is-buffer": "^2.0.0", 126 "@types/is-buffer": "^2.0.0",
@@ -130,7 +130,7 @@ @@ -130,7 +130,7 @@
130 "is-buffer": "^2.0.3", 130 "is-buffer": "^2.0.3",
131 "long": "^4.0.0", 131 "long": "^4.0.0",
132 "process": "^0.11.10", 132 "process": "^0.11.10",
133 - "rhea": "^1.0.18", 133 + "rhea": "^1.0.21",
134 "rhea-promise": "^0.1.15", 134 "rhea-promise": "^0.1.15",
135 "tslib": "^1.10.0" 135 "tslib": "^1.10.0"
136 }, 136 },
@@ -151,18 +151,18 @@ @@ -151,18 +151,18 @@
151 } 151 }
152 }, 152 },
153 "@babel/parser": { 153 "@babel/parser": {
154 - "version": "7.8.4",  
155 - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",  
156 - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", 154 + "version": "7.9.6",
  155 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
  156 + "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==",
157 "dev": true 157 "dev": true
158 }, 158 },
159 "@babel/runtime": { 159 "@babel/runtime": {
160 - "version": "7.8.4",  
161 - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz",  
162 - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", 160 + "version": "7.9.6",
  161 + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz",
  162 + "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==",
163 "dev": true, 163 "dev": true,
164 "requires": { 164 "requires": {
165 - "regenerator-runtime": "^0.13.2" 165 + "regenerator-runtime": "^0.13.4"
166 } 166 }
167 }, 167 },
168 "@google-cloud/paginator": { 168 "@google-cloud/paginator": {
@@ -190,9 +190,9 @@ @@ -190,9 +190,9 @@
190 "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ==" 190 "integrity": "sha512-VccZDcOql77obTnFh0TbNED/6ZbbmHDf8UMNnzO1d5g9V0Htfm4k5cllY8P1tJsRKC3zWYGRLaViiupcgVjBoQ=="
191 }, 191 },
192 "@google-cloud/pubsub": { 192 "@google-cloud/pubsub": {
193 - "version": "1.7.2",  
194 - "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-1.7.2.tgz",  
195 - "integrity": "sha512-/TziioDSV4FS4wKF1sIaQ+1gvE+um83oHz1nRsZ3L87uWSoOciBjJAcocgPjqrpnW441+Nuw4w0QdSUV1Lka/g==", 193 + "version": "1.7.3",
  194 + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-1.7.3.tgz",
  195 + "integrity": "sha512-v+KdeaOS17WtHnsDf2bPGxKDT9HIRPYo3n+WsAEmvAzDHnh8q65mFcuYoQxuy2iRhmN/1ql2a0UU2tAAL7XZ8Q==",
196 "requires": { 196 "requires": {
197 "@google-cloud/paginator": "^2.0.0", 197 "@google-cloud/paginator": "^2.0.0",
198 "@google-cloud/precise-date": "^1.0.0", 198 "@google-cloud/precise-date": "^1.0.0",
@@ -372,9 +372,9 @@ @@ -372,9 +372,9 @@
372 "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" 372 "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
373 }, 373 },
374 "@types/node": { 374 "@types/node": {
375 - "version": "13.13.4",  
376 - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",  
377 - "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==" 375 + "version": "14.0.1",
  376 + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz",
  377 + "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA=="
378 }, 378 },
379 "@types/node-fetch": { 379 "@types/node-fetch": {
380 "version": "2.5.7", 380 "version": "2.5.7",
@@ -449,9 +449,9 @@ @@ -449,9 +449,9 @@
449 } 449 }
450 }, 450 },
451 "amqplib": { 451 "amqplib": {
452 - "version": "0.5.5",  
453 - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.5.tgz",  
454 - "integrity": "sha512-sWx1hbfHbyKMw6bXOK2k6+lHL8TESWxjAx5hG8fBtT7wcxoXNIsFxZMnFyBjxt3yL14vn7WqBDe5U6BGOadtLg==", 452 + "version": "0.5.6",
  453 + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.6.tgz",
  454 + "integrity": "sha512-J4TR0WAMPBHN+tgTuhNsSObfM9eTVTZm/FNw0LyaGfbiLsBxqSameDNYpChUFXW4bnTKHDXy0ab+nuLhumnRrQ==",
455 "requires": { 455 "requires": {
456 "bitsyntax": "~0.1.0", 456 "bitsyntax": "~0.1.0",
457 "bluebird": "^3.5.2", 457 "bluebird": "^3.5.2",
@@ -610,9 +610,9 @@ @@ -610,9 +610,9 @@
610 "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" 610 "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
611 }, 611 },
612 "async-lock": { 612 "async-lock": {
613 - "version": "1.2.2",  
614 - "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.2.2.tgz",  
615 - "integrity": "sha512-uczz62z2fMWOFbyo6rG4NlV2SdxugJT6sZA2QcfB1XaSjEiOh8CuOb/TttyMnYQCda6nkWecJe465tGQDPJiKw==" 613 + "version": "1.2.4",
  614 + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.2.4.tgz",
  615 + "integrity": "sha512-UBQJC2pbeyGutIfYmErGc9RaJYnpZ1FHaxuKwb0ahvGiiCkPUf3p67Io+YLPmmv3RHY+mF6JEtNW8FlHsraAaA=="
616 }, 616 },
617 "asynckit": { 617 "asynckit": {
618 "version": "0.4.0", 618 "version": "0.4.0",
@@ -626,9 +626,9 @@ @@ -626,9 +626,9 @@
626 "dev": true 626 "dev": true
627 }, 627 },
628 "aws-sdk": { 628 "aws-sdk": {
629 - "version": "2.669.0",  
630 - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.669.0.tgz",  
631 - "integrity": "sha512-kuVcSRpDzvkgmeSmMX6Q32eTOb8UeihhUdavMrvUOP6fzSU19cNWS9HAIkYOi/jrEDK85cCZxXjxqE3JGZIGcw==", 629 + "version": "2.677.0",
  630 + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.677.0.tgz",
  631 + "integrity": "sha512-vzQWRh1sgM0HRNmbLXgxnFPySLQrtSNgs9dNQsksGiYrJtf1wYjJSh4UHhekeyMuorQqef3m4AY0vFWsWyZSMg==",
632 "requires": { 632 "requires": {
633 "buffer": "4.9.1", 633 "buffer": "4.9.1",
634 "events": "1.1.1", 634 "events": "1.1.1",
@@ -1136,11 +1136,11 @@ @@ -1136,11 +1136,11 @@
1136 "dev": true 1136 "dev": true
1137 }, 1137 },
1138 "config": { 1138 "config": {
1139 - "version": "3.2.5",  
1140 - "resolved": "https://registry.npmjs.org/config/-/config-3.2.5.tgz",  
1141 - "integrity": "sha512-8itpjyR01lAJanhAlPncBngYRZez/LoRLW8wnGi+6SEcsUyA1wvHvbpIrAJYDJT+W9BScnj4mYoUgbtp9I+0+Q==", 1139 + "version": "3.3.1",
  1140 + "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz",
  1141 + "integrity": "sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==",
1142 "requires": { 1142 "requires": {
1143 - "json5": "^1.0.1" 1143 + "json5": "^2.1.1"
1144 } 1144 }
1145 }, 1145 },
1146 "configstore": { 1146 "configstore": {
@@ -1680,16 +1680,17 @@ @@ -1680,16 +1680,17 @@
1680 "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" 1680 "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
1681 }, 1681 },
1682 "fast-glob": { 1682 "fast-glob": {
1683 - "version": "3.1.1",  
1684 - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz",  
1685 - "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==", 1683 + "version": "3.2.2",
  1684 + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz",
  1685 + "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==",
1686 "dev": true, 1686 "dev": true,
1687 "requires": { 1687 "requires": {
1688 "@nodelib/fs.stat": "^2.0.2", 1688 "@nodelib/fs.stat": "^2.0.2",
1689 "@nodelib/fs.walk": "^1.2.3", 1689 "@nodelib/fs.walk": "^1.2.3",
1690 "glob-parent": "^5.1.0", 1690 "glob-parent": "^5.1.0",
1691 "merge2": "^1.3.0", 1691 "merge2": "^1.3.0",
1692 - "micromatch": "^4.0.2" 1692 + "micromatch": "^4.0.2",
  1693 + "picomatch": "^2.2.1"
1693 }, 1694 },
1694 "dependencies": { 1695 "dependencies": {
1695 "braces": { 1696 "braces": {
@@ -1711,9 +1712,9 @@ @@ -1711,9 +1712,9 @@
1711 } 1712 }
1712 }, 1713 },
1713 "glob-parent": { 1714 "glob-parent": {
1714 - "version": "5.1.0",  
1715 - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",  
1716 - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", 1715 + "version": "5.1.1",
  1716 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
  1717 + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
1717 "dev": true, 1718 "dev": true,
1718 "requires": { 1719 "requires": {
1719 "is-glob": "^4.0.1" 1720 "is-glob": "^4.0.1"
@@ -1768,12 +1769,12 @@ @@ -1768,12 +1769,12 @@
1768 "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw==" 1769 "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
1769 }, 1770 },
1770 "fastq": { 1771 "fastq": {
1771 - "version": "1.6.0",  
1772 - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz",  
1773 - "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", 1772 + "version": "1.8.0",
  1773 + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
  1774 + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
1774 "dev": true, 1775 "dev": true,
1775 "requires": { 1776 "requires": {
1776 - "reusify": "^1.0.0" 1777 + "reusify": "^1.0.4"
1777 } 1778 }
1778 }, 1779 },
1779 "fecha": { 1780 "fecha": {
@@ -3038,11 +3039,18 @@ @@ -3038,11 +3039,18 @@
3038 "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 3039 "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
3039 }, 3040 },
3040 "json5": { 3041 "json5": {
3041 - "version": "1.0.1",  
3042 - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",  
3043 - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", 3042 + "version": "2.1.3",
  3043 + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
  3044 + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
3044 "requires": { 3045 "requires": {
3045 - "minimist": "^1.2.0" 3046 + "minimist": "^1.2.5"
  3047 + },
  3048 + "dependencies": {
  3049 + "minimist": {
  3050 + "version": "1.2.5",
  3051 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
  3052 + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
  3053 + }
3046 } 3054 }
3047 }, 3055 },
3048 "jsonfile": { 3056 "jsonfile": {
@@ -3269,7 +3277,8 @@ @@ -3269,7 +3277,8 @@
3269 "minimist": { 3277 "minimist": {
3270 "version": "1.2.0", 3278 "version": "1.2.0",
3271 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 3279 "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
3272 - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 3280 + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
  3281 + "dev": true
3273 }, 3282 },
3274 "mixin-deep": { 3283 "mixin-deep": {
3275 "version": "1.3.2", 3284 "version": "1.3.2",
@@ -3293,18 +3302,18 @@ @@ -3293,18 +3302,18 @@
3293 } 3302 }
3294 }, 3303 },
3295 "mkdirp": { 3304 "mkdirp": {
3296 - "version": "0.5.1",  
3297 - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",  
3298 - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 3305 + "version": "0.5.5",
  3306 + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
  3307 + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
3299 "dev": true, 3308 "dev": true,
3300 "requires": { 3309 "requires": {
3301 - "minimist": "0.0.8" 3310 + "minimist": "^1.2.5"
3302 }, 3311 },
3303 "dependencies": { 3312 "dependencies": {
3304 "minimist": { 3313 "minimist": {
3305 - "version": "0.0.8",  
3306 - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",  
3307 - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 3314 + "version": "1.2.5",
  3315 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
  3316 + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
3308 "dev": true 3317 "dev": true
3309 } 3318 }
3310 } 3319 }
@@ -3629,9 +3638,9 @@ @@ -3629,9 +3638,9 @@
3629 "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 3638 "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
3630 }, 3639 },
3631 "picomatch": { 3640 "picomatch": {
3632 - "version": "2.2.1",  
3633 - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",  
3634 - "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", 3641 + "version": "2.2.2",
  3642 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
  3643 + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
3635 "dev": true 3644 "dev": true
3636 }, 3645 },
3637 "pify": { 3646 "pify": {
@@ -3641,23 +3650,23 @@ @@ -3641,23 +3650,23 @@
3641 "dev": true 3650 "dev": true
3642 }, 3651 },
3643 "pkg": { 3652 "pkg": {
3644 - "version": "4.4.3",  
3645 - "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.4.3.tgz",  
3646 - "integrity": "sha512-1M0KNVLxNUzr0CGMhccPxx02G05GL3h9czbQGLswRB2gOvHeKAbld+1S5SGDNLOoFt+IYNNxqHtBhZA6Rer7QQ==", 3653 + "version": "4.4.8",
  3654 + "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.4.8.tgz",
  3655 + "integrity": "sha512-Fqqv0iaX48U3CFZxd6Dq6JKe7BrAWbgRAqMJkz/m8W3H5cqJ6suvsUWe5AJPRlN/AhbBYXBJ0XG9QlYPTXcVFA==",
3647 "dev": true, 3656 "dev": true,
3648 "requires": { 3657 "requires": {
3649 - "@babel/parser": "^7.7.5",  
3650 - "@babel/runtime": "^7.7.5", 3658 + "@babel/parser": "^7.9.4",
  3659 + "@babel/runtime": "^7.9.2",
3651 "chalk": "^3.0.0", 3660 "chalk": "^3.0.0",
3652 - "escodegen": "^1.13.0", 3661 + "escodegen": "^1.14.1",
3653 "fs-extra": "^8.1.0", 3662 "fs-extra": "^8.1.0",
3654 "globby": "^11.0.0", 3663 "globby": "^11.0.0",
3655 "into-stream": "^5.1.1", 3664 "into-stream": "^5.1.1",
3656 - "minimist": "^1.2.0", 3665 + "minimist": "^1.2.5",
3657 "multistream": "^2.1.1", 3666 "multistream": "^2.1.1",
3658 - "pkg-fetch": "^2.6.4", 3667 + "pkg-fetch": "^2.6.7",
3659 "progress": "^2.0.3", 3668 "progress": "^2.0.3",
3660 - "resolve": "^1.15.0", 3669 + "resolve": "^1.15.1",
3661 "stream-meter": "^1.0.4" 3670 "stream-meter": "^1.0.4"
3662 }, 3671 },
3663 "dependencies": { 3672 "dependencies": {
@@ -3713,6 +3722,12 @@ @@ -3713,6 +3722,12 @@
3713 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 3722 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
3714 "dev": true 3723 "dev": true
3715 }, 3724 },
  3725 + "minimist": {
  3726 + "version": "1.2.5",
  3727 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
  3728 + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
  3729 + "dev": true
  3730 + },
3716 "supports-color": { 3731 "supports-color": {
3717 "version": "7.1.0", 3732 "version": "7.1.0",
3718 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 3733 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -3725,17 +3740,17 @@ @@ -3725,17 +3740,17 @@
3725 } 3740 }
3726 }, 3741 },
3727 "pkg-fetch": { 3742 "pkg-fetch": {
3728 - "version": "2.6.4",  
3729 - "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-2.6.4.tgz",  
3730 - "integrity": "sha512-4j4jiuo6RRIuD9e9xUE6OQYnIkQCArZjkHXNYsSJjxhJeiHE16MA+rENMblvGLbeWsTY3BPfcYVCGFXzpfJetA==", 3743 + "version": "2.6.8",
  3744 + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-2.6.8.tgz",
  3745 + "integrity": "sha512-CFG7jOeVD38lltLGA7xCJxYsD//GKLjl1P9tc/n9By2a4WEHQjfkBMrYdMS8WOHVP+r9L20fsZNbaKcubDAiQg==",
3731 "dev": true, 3746 "dev": true,
3732 "requires": { 3747 "requires": {
3733 - "@babel/runtime": "^7.7.5", 3748 + "@babel/runtime": "^7.9.2",
3734 "byline": "^5.0.0", 3749 "byline": "^5.0.0",
3735 "chalk": "^3.0.0", 3750 "chalk": "^3.0.0",
3736 "expand-template": "^2.0.3", 3751 "expand-template": "^2.0.3",
3737 "fs-extra": "^8.1.0", 3752 "fs-extra": "^8.1.0",
3738 - "minimist": "^1.2.0", 3753 + "minimist": "^1.2.5",
3739 "progress": "^2.0.3", 3754 "progress": "^2.0.3",
3740 "request": "^2.88.0", 3755 "request": "^2.88.0",
3741 "request-progress": "^3.0.0", 3756 "request-progress": "^3.0.0",
@@ -3795,6 +3810,12 @@ @@ -3795,6 +3810,12 @@
3795 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 3810 "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
3796 "dev": true 3811 "dev": true
3797 }, 3812 },
  3813 + "minimist": {
  3814 + "version": "1.2.5",
  3815 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
  3816 + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
  3817 + "dev": true
  3818 + },
3798 "supports-color": { 3819 "supports-color": {
3799 "version": "7.1.0", 3820 "version": "7.1.0",
3800 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 3821 "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -3858,6 +3879,13 @@ @@ -3858,6 +3879,13 @@
3858 "@types/long": "^4.0.1", 3879 "@types/long": "^4.0.1",
3859 "@types/node": "^13.7.0", 3880 "@types/node": "^13.7.0",
3860 "long": "^4.0.0" 3881 "long": "^4.0.0"
  3882 + },
  3883 + "dependencies": {
  3884 + "@types/node": {
  3885 + "version": "13.13.6",
  3886 + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.6.tgz",
  3887 + "integrity": "sha512-zqRj8ugfROCjXCNbmPBe2mmQ0fJWP9lQaN519hwunOgpHgVykme4G6FW95++dyNFDvJUk4rtExkVkL0eciu5NA=="
  3888 + }
3861 } 3889 }
3862 }, 3890 },
3863 "pseudomap": { 3891 "pseudomap": {
@@ -3963,9 +3991,9 @@ @@ -3963,9 +3991,9 @@
3963 } 3991 }
3964 }, 3992 },
3965 "regenerator-runtime": { 3993 "regenerator-runtime": {
3966 - "version": "0.13.3",  
3967 - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",  
3968 - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", 3994 + "version": "0.13.5",
  3995 + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
  3996 + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
3969 "dev": true 3997 "dev": true
3970 }, 3998 },
3971 "regex-not": { 3999 "regex-not": {
@@ -4057,9 +4085,9 @@ @@ -4057,9 +4085,9 @@
4057 "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 4085 "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
4058 }, 4086 },
4059 "resolve": { 4087 "resolve": {
4060 - "version": "1.15.1",  
4061 - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",  
4062 - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", 4088 + "version": "1.17.0",
  4089 + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
  4090 + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
4063 "dev": true, 4091 "dev": true,
4064 "requires": { 4092 "requires": {
4065 "path-parse": "^1.0.6" 4093 "path-parse": "^1.0.6"
@@ -4103,9 +4131,9 @@ @@ -4103,9 +4131,9 @@
4103 "dev": true 4131 "dev": true
4104 }, 4132 },
4105 "rhea": { 4133 "rhea": {
4106 - "version": "1.0.20",  
4107 - "resolved": "https://registry.npmjs.org/rhea/-/rhea-1.0.20.tgz",  
4108 - "integrity": "sha512-qj4LSEykJ0SEYESQLg9Vee6VXH5xHN1pYj7ozPeUk+l+S1OaGKx1FugAu+g+3pPwK46WXV1PJD9XiRx8+tS4cw==", 4134 + "version": "1.0.21",
  4135 + "resolved": "https://registry.npmjs.org/rhea/-/rhea-1.0.21.tgz",
  4136 + "integrity": "sha512-9ddxyJR0nlWmynukzZTWN+bSYWu7KLHVMkIH/7PpFG5RHfV5t7zXIfZ6rqJSJe9wBAgnNr2Xz41KM2nPujWiFQ==",
4109 "requires": { 4137 "requires": {
4110 "debug": "0.8.0 - 3.5.0" 4138 "debug": "0.8.0 - 3.5.0"
4111 } 4139 }
@@ -4670,9 +4698,9 @@ @@ -4670,9 +4698,9 @@
4670 "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" 4698 "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
4671 }, 4699 },
4672 "tslib": { 4700 "tslib": {
4673 - "version": "1.11.1",  
4674 - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",  
4675 - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" 4701 + "version": "1.13.0",
  4702 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
  4703 + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
4676 }, 4704 },
4677 "tunnel": { 4705 "tunnel": {
4678 "version": "0.0.6", 4706 "version": "0.0.6",
@@ -12,14 +12,14 @@ @@ -12,14 +12,14 @@
12 "start-prod": "NODE_ENV=production nodemon server.js" 12 "start-prod": "NODE_ENV=production nodemon server.js"
13 }, 13 },
14 "dependencies": { 14 "dependencies": {
15 - "config": "^3.2.2", 15 + "@azure/service-bus": "^1.1.7",
  16 + "@google-cloud/pubsub": "^1.7.3",
  17 + "amqplib": "^0.5.6",
  18 + "aws-sdk": "^2.677.0",
  19 + "azure-sb": "^0.11.1",
  20 + "config": "^3.3.1",
16 "js-yaml": "^3.12.0", 21 "js-yaml": "^3.12.0",
17 "kafkajs": "^1.12.0", 22 "kafkajs": "^1.12.0",
18 - "@google-cloud/pubsub": "^1.7.1",  
19 - "aws-sdk": "^2.663.0",  
20 - "amqplib": "^0.5.5",  
21 - "@azure/service-bus": "^1.1.6",  
22 - "azure-sb": "^0.11.1",  
23 "long": "^4.0.0", 23 "long": "^4.0.0",
24 "uuid-parse": "^1.0.0", 24 "uuid-parse": "^1.0.0",
25 "uuid-random": "^1.3.0", 25 "uuid-random": "^1.3.0",
@@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
40 "devDependencies": { 40 "devDependencies": {
41 "fs-extra": "^6.0.1", 41 "fs-extra": "^6.0.1",
42 "nodemon": "^1.17.5", 42 "nodemon": "^1.17.5",
43 - "pkg": "^4.4.0" 43 + "pkg": "^4.4.8"
44 }, 44 },
45 "pkg": { 45 "pkg": {
46 "assets": [ 46 "assets": [
@@ -100,7 +100,7 @@ function AwsSqsProducer() { @@ -100,7 +100,7 @@ function AwsSqsProducer() {
100 const params = { 100 const params = {
101 MaxNumberOfMessages: 10, 101 MaxNumberOfMessages: 10,
102 QueueUrl: requestQueueURL, 102 QueueUrl: requestQueueURL,
103 - WaitTimeSeconds: poolInterval / 1000 103 + WaitTimeSeconds: pollInterval / 1000
104 }; 104 };
105 while (!stopped) { 105 while (!stopped) {
106 let pollStartTs = new Date().getTime(); 106 let pollStartTs = new Date().getTime();
@@ -68,9 +68,8 @@ function RabbitMqProducer() { @@ -68,9 +68,8 @@ function RabbitMqProducer() {
68 (async () => { 68 (async () => {
69 try { 69 try {
70 logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); 70 logger.info('Starting ThingsBoard JavaScript Executor Microservice...');
71 - const url = `amqp://${host}:${port}${vhost}`; 71 + const url = `amqp://${username}:${password}@${host}:${port}${vhost}`;
72 72
73 - amqp.credentials.amqplain(username, password);  
74 connection = await new Promise((resolve, reject) => { 73 connection = await new Promise((resolve, reject) => {
75 amqp.connect(url, function (err, connection) { 74 amqp.connect(url, function (err, connection) {
76 if (err) { 75 if (err) {
@@ -61,15 +61,14 @@ @@ -61,15 +61,14 @@
61 <akka.version>2.6.3</akka.version> 61 <akka.version>2.6.3</akka.version>
62 <californium.version>1.0.2</californium.version> 62 <californium.version>1.0.2</californium.version>
63 <gson.version>2.6.2</gson.version> 63 <gson.version>2.6.2</gson.version>
64 - <velocity.version>1.7</velocity.version>  
65 - <velocity-tools.version>2.0</velocity-tools.version> 64 + <freemarker.version>2.3.30</freemarker.version>
66 <mail.version>1.6.2</mail.version> 65 <mail.version>1.6.2</mail.version>
67 <curator.version>4.2.0</curator.version> 66 <curator.version>4.2.0</curator.version>
68 <zookeeper.version>3.5.5</zookeeper.version> 67 <zookeeper.version>3.5.5</zookeeper.version>
69 <protobuf.version>3.11.4</protobuf.version> 68 <protobuf.version>3.11.4</protobuf.version>
70 <grpc.version>1.22.1</grpc.version> 69 <grpc.version>1.22.1</grpc.version>
71 <lombok.version>1.16.18</lombok.version> 70 <lombok.version>1.16.18</lombok.version>
72 - <paho.client.version>1.1.0</paho.client.version> 71 + <paho.client.version>1.2.4</paho.client.version>
73 <netty.version>4.1.49.Final</netty.version> 72 <netty.version>4.1.49.Final</netty.version>
74 <os-maven-plugin.version>1.5.0</os-maven-plugin.version> 73 <os-maven-plugin.version>1.5.0</os-maven-plugin.version>
75 <rabbitmq.version>4.8.0</rabbitmq.version> 74 <rabbitmq.version>4.8.0</rabbitmq.version>
@@ -565,28 +564,9 @@ @@ -565,28 +564,9 @@
565 <version>${jjwt.version}</version> 564 <version>${jjwt.version}</version>
566 </dependency> 565 </dependency>
567 <dependency> 566 <dependency>
568 - <groupId>org.apache.velocity</groupId>  
569 - <artifactId>velocity</artifactId>  
570 - <version>${velocity.version}</version>  
571 - </dependency>  
572 - <dependency>  
573 - <groupId>org.apache.velocity</groupId>  
574 - <artifactId>velocity-tools</artifactId>  
575 - <version>${velocity-tools.version}</version>  
576 - <exclusions>  
577 - <exclusion>  
578 - <groupId>javax.servlet</groupId>  
579 - <artifactId>servlet-api</artifactId>  
580 - </exclusion>  
581 - <exclusion>  
582 - <groupId>dom4j</groupId>  
583 - <artifactId>dom4j</artifactId>  
584 - </exclusion>  
585 - <exclusion>  
586 - <groupId>antlr</groupId>  
587 - <artifactId>antlr</artifactId>  
588 - </exclusion>  
589 - </exclusions> 567 + <groupId>org.freemarker</groupId>
  568 + <artifactId>freemarker</artifactId>
  569 + <version>${freemarker.version}</version>
590 </dependency> 570 </dependency>
591 <dependency> 571 <dependency>
592 <groupId>org.yaml</groupId> 572 <groupId>org.yaml</groupId>
@@ -975,11 +955,6 @@ @@ -975,11 +955,6 @@
975 <version>${commons-collections.version}</version> 955 <version>${commons-collections.version}</version>
976 </dependency> 956 </dependency>
977 <dependency> 957 <dependency>
978 - <groupId>org.yaml</groupId>  
979 - <artifactId>snakeyaml</artifactId>  
980 - <version>${snakeyaml.version}</version>  
981 - </dependency>  
982 - <dependency>  
983 <groupId>org.apache.struts</groupId> 958 <groupId>org.apache.struts</groupId>
984 <artifactId>struts-core</artifactId> 959 <artifactId>struts-core</artifactId>
985 <version>${struts.version}</version> 960 <version>${struts.version}</version>
@@ -147,6 +147,16 @@ public class TbSendEmailNode implements TbNode { @@ -147,6 +147,16 @@ public class TbSendEmailNode implements TbNode {
147 if (this.config.isEnableTls() && StringUtils.isNoneEmpty(this.config.getTlsVersion())) { 147 if (this.config.isEnableTls() && StringUtils.isNoneEmpty(this.config.getTlsVersion())) {
148 javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", this.config.getTlsVersion()); 148 javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", this.config.getTlsVersion());
149 } 149 }
  150 + if (this.config.isEnableProxy()) {
  151 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.host", config.getProxyHost());
  152 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.port", config.getProxyPort());
  153 + if (StringUtils.isNoneEmpty(config.getProxyUser())) {
  154 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.user", config.getProxyUser());
  155 + }
  156 + if (StringUtils.isNoneEmpty(config.getProxyPassword())) {
  157 + javaMailProperties.put(MAIL_PROP + protocol + ".proxy.password", config.getProxyPassword());
  158 + }
  159 + }
150 return javaMailProperties; 160 return javaMailProperties;
151 } 161 }
152 } 162 }
@@ -30,6 +30,11 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration { @@ -30,6 +30,11 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration {
30 private int timeout; 30 private int timeout;
31 private boolean enableTls; 31 private boolean enableTls;
32 private String tlsVersion; 32 private String tlsVersion;
  33 + private boolean enableProxy;
  34 + private String proxyHost;
  35 + private String proxyPort;
  36 + private String proxyUser;
  37 + private String proxyPassword;
33 38
34 @Override 39 @Override
35 public TbSendEmailNodeConfiguration defaultConfiguration() { 40 public TbSendEmailNodeConfiguration defaultConfiguration() {
@@ -41,6 +46,7 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration { @@ -41,6 +46,7 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration {
41 configuration.setTimeout(10000); 46 configuration.setTimeout(10000);
42 configuration.setEnableTls(false); 47 configuration.setEnableTls(false);
43 configuration.setTlsVersion("TLSv1.2"); 48 configuration.setTlsVersion("TLSv1.2");
  49 + configuration.setEnableProxy(false);
44 return configuration; 50 return configuration;
45 } 51 }
46 } 52 }
@@ -48,7 +48,7 @@ public class TbSynchronizationBeginNode implements TbNode { @@ -48,7 +48,7 @@ public class TbSynchronizationBeginNode implements TbNode {
48 48
49 @Override 49 @Override
50 public void onMsg(TbContext ctx, TbMsg msg) { 50 public void onMsg(TbContext ctx, TbMsg msg) {
51 - log.warn("Synchronization Start/End nodes are deprecated since TB 2.5. Use queue with submit strategy SEQUENTIAL_WITHIN_ORIGINATOR instead."); 51 + log.warn("Synchronization Start/End nodes are deprecated since TB 2.5. Use queue with submit strategy SEQUENTIAL_BY_ORIGINATOR instead.");
52 ctx.tellSuccess(msg); 52 ctx.tellSuccess(msg);
53 } 53 }
54 54
@@ -49,7 +49,7 @@ public class TbSynchronizationEndNode implements TbNode { @@ -49,7 +49,7 @@ public class TbSynchronizationEndNode implements TbNode {
49 49
50 @Override 50 @Override
51 public void onMsg(TbContext ctx, TbMsg msg) { 51 public void onMsg(TbContext ctx, TbMsg msg) {
52 - log.warn("Synchronization Start/End nodes are deprecated since TB 2.5. Use queue with submit strategy SEQUENTIAL_WITHIN_ORIGINATOR instead."); 52 + log.warn("Synchronization Start/End nodes are deprecated since TB 2.5. Use queue with submit strategy SEQUENTIAL_BY_ORIGINATOR instead.");
53 ctx.tellSuccess(msg); 53 ctx.tellSuccess(msg);
54 } 54 }
55 55
@@ -155,19 +155,19 @@ queue: @@ -155,19 +155,19 @@ queue:
155 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" 155 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
156 stats: 156 stats:
157 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" 157 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
158 - print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" 158 + print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
159 queues: 159 queues:
160 - - name: "Main" 160 + - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}"
161 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" 161 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
162 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" 162 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
163 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" 163 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
164 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}" 164 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
165 submit-strategy: 165 submit-strategy:
166 - type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 166 + type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
167 # For BATCH only 167 # For BATCH only
168 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch 168 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
169 processing-strategy: 169 processing-strategy:
170 - type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 170 + type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
171 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 171 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
172 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited 172 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
173 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 173 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@@ -175,10 +175,10 @@ queue: @@ -175,10 +175,10 @@ queue:
175 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" 175 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
176 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" 176 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
177 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" 177 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
178 - partitions: "${TB_QUEUE_RE_HP_PARTITIONS:3}" 178 + partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
179 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}" 179 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
180 submit-strategy: 180 submit-strategy:
181 - type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_WITHIN_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 181 + type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
182 # For BATCH only 182 # For BATCH only
183 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch 183 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
184 processing-strategy: 184 processing-strategy:
@@ -187,6 +187,21 @@ queue: @@ -187,6 +187,21 @@ queue:
187 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited 187 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
188 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 188 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
189 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries; 189 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  190 + - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
  191 + topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
  192 + poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
  193 + partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
  194 + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
  195 + submit-strategy:
  196 + type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
  197 + # For BATCH only
  198 + batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
  199 + processing-strategy:
  200 + type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  201 + # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  202 + retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
  203 + failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
  204 + pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
190 transport: 205 transport:
191 # For high priority notifications that require minimum latency and processing time 206 # For high priority notifications that require minimum latency and processing time
192 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" 207 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
@@ -156,19 +156,19 @@ queue: @@ -156,19 +156,19 @@ queue:
156 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" 156 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
157 stats: 157 stats:
158 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" 158 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
159 - print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" 159 + print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
160 queues: 160 queues:
161 - - name: "Main" 161 + - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}"
162 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" 162 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
163 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" 163 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
164 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" 164 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
165 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}" 165 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
166 submit-strategy: 166 submit-strategy:
167 - type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 167 + type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
168 # For BATCH only 168 # For BATCH only
169 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch 169 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
170 processing-strategy: 170 processing-strategy:
171 - type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 171 + type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
172 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 172 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
173 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited 173 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
174 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 174 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@@ -176,10 +176,10 @@ queue: @@ -176,10 +176,10 @@ queue:
176 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" 176 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
177 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" 177 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
178 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" 178 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
179 - partitions: "${TB_QUEUE_RE_HP_PARTITIONS:3}" 179 + partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
180 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}" 180 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
181 submit-strategy: 181 submit-strategy:
182 - type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_WITHIN_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 182 + type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
183 # For BATCH only 183 # For BATCH only
184 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch 184 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
185 processing-strategy: 185 processing-strategy:
@@ -188,6 +188,21 @@ queue: @@ -188,6 +188,21 @@ queue:
188 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited 188 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
189 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 189 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
190 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries; 190 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  191 + - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
  192 + topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
  193 + poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
  194 + partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
  195 + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
  196 + submit-strategy:
  197 + type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
  198 + # For BATCH only
  199 + batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
  200 + processing-strategy:
  201 + type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  202 + # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  203 + retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
  204 + failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
  205 + pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
191 transport: 206 transport:
192 # For high priority notifications that require minimum latency and processing time 207 # For high priority notifications that require minimum latency and processing time
193 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" 208 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
@@ -176,19 +176,19 @@ queue: @@ -176,19 +176,19 @@ queue:
176 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" 176 pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}"
177 stats: 177 stats:
178 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" 178 enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}"
179 - print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:10000}" 179 + print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}"
180 queues: 180 queues:
181 - - name: "Main" 181 + - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}"
182 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" 182 topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}"
183 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" 183 poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}"
184 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" 184 partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}"
185 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}" 185 pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:60000}"
186 submit-strategy: 186 submit-strategy:
187 - type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 187 + type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
188 # For BATCH only 188 # For BATCH only
189 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch 189 batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
190 processing-strategy: 190 processing-strategy:
191 - type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 191 + type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
192 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT 192 # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
193 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited 193 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
194 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 194 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@@ -196,10 +196,10 @@ queue: @@ -196,10 +196,10 @@ queue:
196 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" 196 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
197 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" 197 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
198 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" 198 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
199 - partitions: "${TB_QUEUE_RE_HP_PARTITIONS:3}" 199 + partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}"
200 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}" 200 pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:60000}"
201 submit-strategy: 201 submit-strategy:
202 - type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_WITHIN_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_WITHIN_ORIGINATOR, SEQUENTIAL_WITHIN_TENANT, SEQUENTIAL 202 + type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
203 # For BATCH only 203 # For BATCH only
204 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch 204 batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
205 processing-strategy: 205 processing-strategy:
@@ -208,6 +208,21 @@ queue: @@ -208,6 +208,21 @@ queue:
208 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited 208 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
209 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 209 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
210 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries; 210 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  211 + - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
  212 + topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
  213 + poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
  214 + partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}"
  215 + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:60000}"
  216 + submit-strategy:
  217 + type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL
  218 + # For BATCH only
  219 + batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
  220 + processing-strategy:
  221 + type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  222 + # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
  223 + retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
  224 + failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
  225 + pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
211 transport: 226 transport:
212 # For high priority notifications that require minimum latency and processing time 227 # For high priority notifications that require minimum latency and processing time
213 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" 228 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
@@ -7969,14 +7969,6 @@ @@ -7969,14 +7969,6 @@
7969 "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz", 7969 "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
7970 "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ==" 7970 "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ=="
7971 }, 7971 },
7972 - "leaflet-geometryutil": {  
7973 - "version": "0.9.3",  
7974 - "resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.3.tgz",  
7975 - "integrity": "sha512-Wi6YvfNx/Xu9q35AEfXpsUXmIFLen/MO+C2qimxHRnjyeyOxBhdcZa6kSiReaOX0cGK7yQInqrzz0dkIqZ8Dpg==",  
7976 - "requires": {  
7977 - "leaflet": ">=0.7.0"  
7978 - }  
7979 - },  
7980 "leaflet-polylinedecorator": { 7972 "leaflet-polylinedecorator": {
7981 "version": "1.6.0", 7973 "version": "1.6.0",
7982 "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz", 7974 "resolved": "https://registry.npmjs.org/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz",
@@ -58,7 +58,6 @@ @@ -58,7 +58,6 @@
58 "jstree-bootstrap-theme": "^1.0.1", 58 "jstree-bootstrap-theme": "^1.0.1",
59 "jszip": "^3.4.0", 59 "jszip": "^3.4.0",
60 "leaflet": "^1.6.0", 60 "leaflet": "^1.6.0",
61 - "leaflet-geometryutil": "^0.9.3",  
62 "leaflet-polylinedecorator": "^1.6.0", 61 "leaflet-polylinedecorator": "^1.6.0",
63 "leaflet-providers": "^1.9.1", 62 "leaflet-providers": "^1.9.1",
64 "leaflet.gridlayer.googlemutant": "0.8.0", 63 "leaflet.gridlayer.googlemutant": "0.8.0",
@@ -496,7 +496,7 @@ export function padValue(val: any, dec: number): string { @@ -496,7 +496,7 @@ export function padValue(val: any, dec: number): string {
496 val = Math.abs(val); 496 val = Math.abs(val);
497 497
498 if (dec > 0) { 498 if (dec > 0) {
499 - strVal = val.toFixed(dec).toString() 499 + strVal = val.toFixed(dec);
500 } else { 500 } else {
501 strVal = Math.round(val).toString(); 501 strVal = Math.round(val).toString();
502 } 502 }
@@ -18,7 +18,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -18,7 +18,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
18 import { 18 import {
19 Component, 19 Component,
20 ComponentFactory, 20 ComponentFactory,
21 - ComponentRef, 21 + ComponentRef, HostBinding,
22 Inject, 22 Inject,
23 Injector, 23 Injector,
24 OnDestroy, 24 OnDestroy,
@@ -46,6 +46,8 @@ export interface CustomDialogContainerData { @@ -46,6 +46,8 @@ export interface CustomDialogContainerData {
46 }) 46 })
47 export class CustomDialogContainerComponent extends DialogComponent<CustomDialogContainerComponent> implements OnDestroy { 47 export class CustomDialogContainerComponent extends DialogComponent<CustomDialogContainerComponent> implements OnDestroy {
48 48
  49 + @HostBinding('style.height') height = '0px';
  50 +
49 private readonly customComponentRef: ComponentRef<CustomDialogComponent>; 51 private readonly customComponentRef: ComponentRef<CustomDialogComponent>;
50 52
51 constructor(protected store: Store<AppState>, 53 constructor(protected store: Store<AppState>,
@@ -20,12 +20,12 @@ import 'leaflet-providers'; @@ -20,12 +20,12 @@ import 'leaflet-providers';
20 import 'leaflet.markercluster/dist/leaflet.markercluster'; 20 import 'leaflet.markercluster/dist/leaflet.markercluster';
21 21
22 import { 22 import {
23 - FormattedData,  
24 - MapSettings,  
25 - MarkerSettings,  
26 - PolygonSettings,  
27 - PolylineSettings,  
28 - UnitedMapSettings 23 + FormattedData,
  24 + MapSettings,
  25 + MarkerSettings,
  26 + PolygonSettings,
  27 + PolylineSettings,
  28 + UnitedMapSettings
29 } from './map-models'; 29 } from './map-models';
30 import { Marker } from './markers'; 30 import { Marker } from './markers';
31 import { BehaviorSubject, Observable } from 'rxjs'; 31 import { BehaviorSubject, Observable } from 'rxjs';
@@ -345,12 +345,12 @@ export default abstract class LeafletMap { @@ -345,12 +345,12 @@ export default abstract class LeafletMap {
345 345
346 // Polyline 346 // Polyline
347 347
348 - updatePolylines(polyData: FormattedData[][]) {  
349 - polyData.forEach((data: FormattedData[]) => {  
350 - if (data.length) {  
351 - const dataSource = polyData.map(arr => arr[0]);  
352 - if (this.polylines.get(data[0].entityName)) {  
353 - this.updatePolyline(data[0].entityName, data, dataSource, this.options); 348 + updatePolylines(polyData: FormattedData[][], data?: FormattedData) {
  349 + polyData.forEach((dataSource) => {
  350 + if (dataSource.length) {
  351 + data = data || dataSource[0];
  352 + if (this.polylines.get(data.$datasource.entityName)) {
  353 + this.updatePolyline(data, dataSource, this.options);
354 } 354 }
355 else { 355 else {
356 this.createPolyline(data, dataSource, this.options); 356 this.createPolyline(data, dataSource, this.options);
@@ -359,28 +359,27 @@ export default abstract class LeafletMap { @@ -359,28 +359,27 @@ export default abstract class LeafletMap {
359 }) 359 })
360 } 360 }
361 361
362 - createPolyline(data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) {  
363 - if (data.length)  
364 - this.ready$.subscribe(() => {  
365 - const poly = new Polyline(this.map,  
366 - data.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);  
367 - const bounds = poly.leafletPoly.getBounds();  
368 - this.fitBounds(bounds);  
369 - this.polylines.set(data[0].entityName, poly);  
370 - }); 362 + createPolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
  363 + this.ready$.subscribe(() => {
  364 + const poly = new Polyline(this.map,
  365 + dataSources.map(el => this.convertPosition(el)).filter(el => !!el), data, dataSources, settings);
  366 + const bounds = poly.leafletPoly.getBounds();
  367 + this.fitBounds(bounds);
  368 + this.polylines.set(data.$datasource.entityName, poly);
  369 + });
371 } 370 }
372 371
373 - updatePolyline(key: string, data: FormattedData[], dataSources: FormattedData[], settings: PolylineSettings) { 372 + updatePolyline(data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
374 this.ready$.subscribe(() => { 373 this.ready$.subscribe(() => {
375 - const poly = this.polylines.get(key); 374 + const poly = this.polylines.get(data.entityName);
376 const oldBounds = poly.leafletPoly.getBounds(); 375 const oldBounds = poly.leafletPoly.getBounds();
377 - poly.updatePolyline(settings, data.map(el => this.convertPosition(el)), dataSources); 376 + poly.updatePolyline(settings, data.map(el => this.convertPosition(el)).filter(el => !!el), dataSources);
378 const newBounds = poly.leafletPoly.getBounds(); 377 const newBounds = poly.leafletPoly.getBounds();
379 if (oldBounds.toBBoxString() !== newBounds.toBBoxString()) { 378 if (oldBounds.toBBoxString() !== newBounds.toBBoxString()) {
380 this.fitBounds(newBounds); 379 this.fitBounds(newBounds);
381 } 380 }
382 }); 381 });
383 - } 382 + }Я
384 383
385 // Polygon 384 // Polygon
386 385
@@ -14,22 +14,28 @@ @@ -14,22 +14,28 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { MapProviders, UnitedMapSettings, providerSets, hereProviders, defaultSettings } from './map-models'; 17 +import { defaultSettings, hereProviders, MapProviders, providerSets, UnitedMapSettings } from './map-models';
18 import LeafletMap from './leaflet-map'; 18 import LeafletMap from './leaflet-map';
19 import { 19 import {
20 - commonMapSettingsSchema,  
21 - routeMapSettingsSchema,  
22 - markerClusteringSettingsSchema,  
23 - markerClusteringSettingsSchemaLeaflet,  
24 - mapProviderSchema,  
25 - mapPolygonSchema 20 + commonMapSettingsSchema,
  21 + mapPolygonSchema,
  22 + mapProviderSchema,
  23 + markerClusteringSettingsSchema,
  24 + markerClusteringSettingsSchemaLeaflet,
  25 + routeMapSettingsSchema
26 } from './schemes'; 26 } from './schemes';
27 -import { MapWidgetStaticInterface, MapWidgetInterface } from './map-widget.interface';  
28 -import { initSchema, addToSchema, mergeSchemes, addCondition, addGroupInfo } from '@core/schema-utils'; 27 +import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
  28 +import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
29 import { of, Subject } from 'rxjs'; 29 import { of, Subject } from 'rxjs';
30 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 30 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
31 import { getDefCenterPosition, parseArray, parseData, parseFunction, parseWithTranslation } from './maps-utils'; 31 import { getDefCenterPosition, parseArray, parseData, parseFunction, parseWithTranslation } from './maps-utils';
32 -import { JsonSettingsSchema, WidgetActionDescriptor, DatasourceType, widgetType, Datasource } from '@shared/models/widget.models'; 32 +import {
  33 + Datasource,
  34 + DatasourceType,
  35 + JsonSettingsSchema,
  36 + WidgetActionDescriptor,
  37 + widgetType
  38 +} from '@shared/models/widget.models';
33 import { EntityId } from '@shared/models/id/entity-id'; 39 import { EntityId } from '@shared/models/id/entity-id';
34 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; 40 import { AttributeScope, DataKeyType, LatestTelemetry } from '@shared/models/telemetry/telemetry.models';
35 import { AttributeService } from '@core/http/attribute.service'; 41 import { AttributeService } from '@core/http/attribute.service';
@@ -39,7 +45,13 @@ import { UtilsService } from '@core/services/utils.service'; @@ -39,7 +45,13 @@ import { UtilsService } from '@core/services/utils.service';
39 // @dynamic 45 // @dynamic
40 export class MapWidgetController implements MapWidgetInterface { 46 export class MapWidgetController implements MapWidgetInterface {
41 47
42 - constructor(public mapProvider: MapProviders, private drawRoutes: boolean, public ctx: WidgetContext, $element: HTMLElement, isEdit?) { 48 + constructor(
  49 + public mapProvider: MapProviders,
  50 + private drawRoutes: boolean,
  51 + public ctx: WidgetContext,
  52 + $element: HTMLElement,
  53 + isEdit?: boolean
  54 + ) {
43 if (this.map) { 55 if (this.map) {
44 this.map.map.remove(); 56 this.map.map.remove();
45 delete this.map; 57 delete this.map;
@@ -20,7 +20,7 @@ import { Datasource } from '@app/shared/models/widget.models'; @@ -20,7 +20,7 @@ import { Datasource } from '@app/shared/models/widget.models';
20 import _ from 'lodash'; 20 import _ from 'lodash';
21 import { Observable, Observer, of } from 'rxjs'; 21 import { Observable, Observer, of } from 'rxjs';
22 import { map } from 'rxjs/operators'; 22 import { map } from 'rxjs/operators';
23 -import { createLabelFromDatasource, hashCode, padValue } from '@core/utils'; 23 +import { createLabelFromDatasource, hashCode, isNumber, isUndefined, padValue } from '@core/utils';
24 24
25 export function createTooltip(target: L.Layer, 25 export function createTooltip(target: L.Layer,
26 settings: MarkerSettings | PolylineSettings | PolygonSettings, 26 settings: MarkerSettings | PolylineSettings | PolygonSettings,
@@ -43,8 +43,9 @@ export function createTooltip(target: L.Layer, @@ -43,8 +43,9 @@ export function createTooltip(target: L.Layer,
43 const actions = document.getElementsByClassName('tb-custom-action'); 43 const actions = document.getElementsByClassName('tb-custom-action');
44 Array.from(actions).forEach( 44 Array.from(actions).forEach(
45 (element: HTMLElement) => { 45 (element: HTMLElement) => {
46 - if (element && settings.tooltipAction[element.id]) {  
47 - element.addEventListener('click', ($event) => settings.tooltipAction[element.id]($event, datasource)); 46 + const actionName = element.getAttribute('data-action-name');
  47 + if (element && settings.tooltipAction[actionName]) {
  48 + element.addEventListener('click', ($event) => settings.tooltipAction[actionName]($event, datasource));
48 } 49 }
49 }); 50 });
50 }); 51 });
@@ -55,10 +56,26 @@ export function getRatio(firsMoment: number, secondMoment: number, intermediateM @@ -55,10 +56,26 @@ export function getRatio(firsMoment: number, secondMoment: number, intermediateM
55 return (intermediateMoment - firsMoment) / (secondMoment - firsMoment); 56 return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
56 } 57 }
57 58
58 -export function findAngle(startPoint, endPoint) {  
59 - let angle = -Math.atan2(endPoint.latitude - startPoint.latitude, endPoint.longitude - startPoint.longitude);  
60 - angle = angle * 180 / Math.PI;  
61 - return parseInt(angle.toFixed(2), 10); 59 +export function interpolateOnLineSegment(
  60 + pointA: FormattedData,
  61 + oointB: FormattedData,
  62 + latKeyName: string,
  63 + lngKeyName: string,
  64 + ratio: number
  65 +): { [key: string]: number } {
  66 + return {
  67 + [latKeyName]: (pointA[latKeyName] + (oointB[latKeyName] - pointA[latKeyName]) * ratio),
  68 + [lngKeyName]: (pointA[lngKeyName] + (oointB[lngKeyName] - pointA[lngKeyName]) * ratio)
  69 + };
  70 +}
  71 +
  72 +export function findAngle(startPoint: FormattedData, endPoint: FormattedData, latKeyName: string, lngKeyName: string): number {
  73 + if(isUndefined(startPoint) || isUndefined(endPoint)){
  74 + return 0;
  75 + }
  76 + let angle = -Math.atan2(endPoint[latKeyName] - startPoint[latKeyName], endPoint[lngKeyName] - startPoint[lngKeyName]);
  77 + angle = angle * 180 / Math.PI;
  78 + return parseInt(angle.toFixed(2), 10);
62 } 79 }
63 80
64 81
@@ -111,38 +128,81 @@ export function aspectCache(imageUrl: string): Observable<number> { @@ -111,38 +128,81 @@ export function aspectCache(imageUrl: string): Observable<number> {
111 128
112 export type TranslateFunc = (key: string, defaultTranslation?: string) => string; 129 export type TranslateFunc = (key: string, defaultTranslation?: string) => string;
113 130
  131 +const varsRegex = /\${([^}]*)}/g;
  132 +const linkActionRegex = /<link-act name=['"]([^['"]*)['"]>([^<]*)<\/link-act>/g;
  133 +const buttonActionRegex = /<button-act name=['"]([^['"]*)['"]>([^<]*)<\/button-act>/g;
  134 +
  135 +function createLinkElement(actionName: string, actionText: string): string {
  136 + return `<a href="#" class="tb-custom-action" data-action-name=${actionName}>${actionText}</a>`;
  137 +}
  138 +
  139 +function createButtonElement(actionName: string, actionText: string) {
  140 + return `<button mat-button class="tb-custom-action" data-action-name=${actionName}>${actionText}</button>`;
  141 +}
  142 +
114 function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any }, 143 function parseTemplate(template: string, data: { $datasource?: Datasource, [key: string]: any },
115 - translateFn?: TranslateFunc) { 144 + translateFn?: TranslateFunc) {
116 let res = ''; 145 let res = '';
117 try { 146 try {
118 - if (template.match(/<link-act/g)) {  
119 - template = template.replace(/<link-act/g, '<a href="#"').replace(/link-act>/g, 'a>')  
120 - .replace(/name=(['"])(.*?)(['"])/g, `class='tb-custom-action' id='$2'`);  
121 - }  
122 if (translateFn) { 147 if (translateFn) {
123 template = translateFn(template); 148 template = translateFn(template);
124 } 149 }
125 template = createLabelFromDatasource(data.$datasource, template); 150 template = createLabelFromDatasource(data.$datasource, template);
126 - const formatted = template.match(/\${([^}]*):\d*}/g);  
127 - if (formatted)  
128 - formatted.forEach(value => {  
129 - const [variable, digits] = value.replace('${', '').replace('}', '').split(':');  
130 - data[variable] = padValue(data[variable], +digits);  
131 - if (data[variable] === 'NaN') data[variable] = '';  
132 - template = template.replace(value, '${' + variable + '}');  
133 - });  
134 - const variables = template.match(/\${.*?}/g);  
135 - if (variables) {  
136 - variables.forEach(variable => {  
137 - variable = variable.replace('${', '').replace('}', '');  
138 - if (!data[variable])  
139 - data[variable] = '';  
140 - }) 151 +
  152 + let match = varsRegex.exec(template);
  153 + while (match !== null) {
  154 + const variable = match[0];
  155 + let label = match[1];
  156 + let valDec = 2;
  157 + const splitValues = label.split(':');
  158 + if (splitValues.length > 1) {
  159 + label = splitValues[0];
  160 + valDec = parseFloat(splitValues[1]);
  161 + }
  162 +
  163 + if (label.startsWith('#')) {
  164 + const keyIndexStr = label.substring(1);
  165 + const n = Math.floor(Number(keyIndexStr));
  166 + if (String(n) === keyIndexStr && n >= 0) {
  167 + label = data.$datasource.dataKeys[n].label;
  168 + }
  169 + }
  170 +
  171 + const value = data[label] || '';
  172 + let textValue: string;
  173 + if (isNumber(value)) {
  174 + textValue = padValue(value, valDec);
  175 + } else {
  176 + textValue = value;
  177 + }
  178 + template = template.split(variable).join(textValue);
  179 + match = varsRegex.exec(template);
141 } 180 }
  181 +
  182 + let actionTags: string;
  183 + let actionText: string;
  184 + let actionName: string;
  185 + let action: string;
  186 +
  187 + match = linkActionRegex.exec(template);
  188 + while (match !== null) {
  189 + [actionTags, actionName, actionText] = match;
  190 + action = createLinkElement(actionName, actionText);
  191 + template = template.split(actionTags).join(action);
  192 + match = linkActionRegex.exec(template);
  193 + }
  194 +
  195 + match = buttonActionRegex.exec(template);
  196 + while (match !== null) {
  197 + [actionTags, actionName, actionText] = match;
  198 + action = createButtonElement(actionName, actionText);
  199 + template = template.split(actionTags).join(action);
  200 + match = buttonActionRegex.exec(template);
  201 + }
  202 +
142 const compiled = _.template(template); 203 const compiled = _.template(template);
143 res = compiled(data); 204 res = compiled(data);
144 - }  
145 - catch (ex) { 205 + } catch (ex) {
146 console.log(ex, template) 206 console.log(ex, template)
147 } 207 }
148 return res; 208 return res;
@@ -14,18 +14,18 @@ @@ -14,18 +14,18 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L, { LatLngExpression, LatLngTuple, LeafletMouseEvent } from 'leaflet'; 17 +import L, { LatLngExpression, LeafletMouseEvent } from 'leaflet';
18 import { createTooltip, parseWithTranslation, safeExecute } from './maps-utils'; 18 import { createTooltip, parseWithTranslation, safeExecute } from './maps-utils';
19 import { FormattedData, PolygonSettings } from './map-models'; 19 import { FormattedData, PolygonSettings } from './map-models';
20 20
21 export class Polygon { 21 export class Polygon {
22 22
23 leafletPoly: L.Polygon; 23 leafletPoly: L.Polygon;
24 - tooltip;  
25 - data;  
26 - dataSources; 24 + tooltip: L.Popup;
  25 + data: FormattedData;
  26 + dataSources: FormattedData[];
27 27
28 - constructor(public map, polyData: FormattedData, dataSources, private settings: PolygonSettings) { 28 + constructor(public map, polyData: FormattedData, dataSources: FormattedData[], private settings: PolygonSettings) {
29 this.dataSources = dataSources; 29 this.dataSources = dataSources;
30 this.data = polyData; 30 this.data = polyData;
31 const polygonColor = this.getPolygonColor(settings); 31 const polygonColor = this.getPolygonColor(settings);
@@ -61,7 +61,7 @@ export class Polygon { @@ -61,7 +61,7 @@ export class Polygon {
61 this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true)); 61 this.tooltip.setContent(parseWithTranslation.parseTemplate(pattern, data, true));
62 } 62 }
63 63
64 - updatePolygon(data:{[coordinates:string]: LatLngTuple[]}, dataSources: FormattedData[], settings: PolygonSettings) { 64 + updatePolygon(data: FormattedData, dataSources: FormattedData[], settings: PolygonSettings) {
65 this.data = data; 65 this.data = data;
66 this.dataSources = dataSources; 66 this.dataSources = dataSources;
67 this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]); 67 this.leafletPoly.setLatLngs(data[this.settings.polygonKeyName]);
@@ -17,82 +17,83 @@ @@ -17,82 +17,83 @@
17 import L, { PolylineDecoratorOptions } from 'leaflet'; 17 import L, { PolylineDecoratorOptions } from 'leaflet';
18 import 'leaflet-polylinedecorator'; 18 import 'leaflet-polylinedecorator';
19 19
20 -import { PolylineSettings } from './map-models'; 20 +import { FormattedData, PolylineSettings } from './map-models';
21 import { safeExecute } from '@home/components/widget/lib/maps/maps-utils'; 21 import { safeExecute } from '@home/components/widget/lib/maps/maps-utils';
22 22
23 export class Polyline { 23 export class Polyline {
24 24
25 - leafletPoly: L.Polyline;  
26 - polylineDecorator: L.PolylineDecorator;  
27 - dataSources;  
28 - data; 25 + leafletPoly: L.Polyline;
  26 + polylineDecorator: L.PolylineDecorator;
  27 + dataSources: FormattedData[];
  28 + data: FormattedData;
29 29
30 - constructor(private map: L.Map, locations, data, dataSources, settings: PolylineSettings) {  
31 - this.dataSources = dataSources;  
32 - this.data = data; 30 + constructor(private map: L.Map, locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
  31 + this.dataSources = dataSources;
  32 + this.data = data;
33 33
34 - this.leafletPoly = L.polyline(locations,  
35 - this.getPolyStyle(settings)  
36 - ).addTo(this.map); 34 + this.leafletPoly = L.polyline(locations,
  35 + this.getPolyStyle(settings)
  36 + ).addTo(this.map);
37 37
38 - if (settings.usePolylineDecorator) {  
39 - this.polylineDecorator = L.polylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);  
40 - } 38 + if (settings.usePolylineDecorator) {
  39 + this.polylineDecorator = L.polylineDecorator(this.leafletPoly, this.getDecoratorSettings(settings)).addTo(this.map);
41 } 40 }
  41 + }
42 42
43 - getDecoratorSettings(settings: PolylineSettings): PolylineDecoratorOptions {  
44 - return {  
45 - patterns: [  
46 - {  
47 - offset: settings.decoratorOffset,  
48 - endOffset: settings.endDecoratorOffset,  
49 - repeat: settings.decoratorRepeat,  
50 - symbol: L.Symbol[settings.decoratorSymbol]({  
51 - pixelSize: settings.decoratorSymbolSize,  
52 - polygon: false,  
53 - pathOptions: {  
54 - color: settings.useDecoratorCustomColor ? settings.decoratorCustomColor : this.getPolyStyle(settings).color,  
55 - stroke: true  
56 - }  
57 - })  
58 - }  
59 - ],  
60 - interactive: false,  
61 - } as PolylineDecoratorOptions 43 + getDecoratorSettings(settings: PolylineSettings): PolylineDecoratorOptions {
  44 + return {
  45 + patterns: [
  46 + {
  47 + offset: settings.decoratorOffset,
  48 + endOffset: settings.endDecoratorOffset,
  49 + repeat: settings.decoratorRepeat,
  50 + symbol: L.Symbol[settings.decoratorSymbol]({
  51 + pixelSize: settings.decoratorSymbolSize,
  52 + polygon: false,
  53 + pathOptions: {
  54 + color: settings.useDecoratorCustomColor ? settings.decoratorCustomColor : this.getPolyStyle(settings).color,
  55 + stroke: true
  56 + }
  57 + })
  58 + }
  59 + ]
62 } 60 }
  61 + }
63 62
64 - updatePolyline(settings, data, dataSources) {  
65 - this.data = data;  
66 - this.dataSources = dataSources;  
67 - this.leafletPoly.setStyle(this.getPolyStyle(settings));  
68 - // this.setPolylineLatLngs(data);  
69 - if (this.polylineDecorator)  
70 - this.polylineDecorator.setPaths(this.leafletPoly);  
71 - } 63 + updatePolyline(locations: L.LatLng[], data: FormattedData, dataSources: FormattedData[], settings: PolylineSettings) {
  64 + this.data = data;
  65 + this.dataSources = dataSources;
  66 + this.leafletPoly.setLatLngs(locations);
  67 + this.leafletPoly.setStyle(this.getPolyStyle(settings));
  68 + // this.setPolylineLatLngs(data);
  69 + if (this.polylineDecorator)
  70 + this.polylineDecorator.setPaths(this.leafletPoly);
  71 + }
72 72
73 - getPolyStyle(settings: PolylineSettings): L.PolylineOptions {  
74 - return {  
75 - color: settings.useColorFunction ?  
76 - safeExecute(settings.colorFunction,  
77 - [this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.color,  
78 - opacity: settings.useStrokeOpacityFunction ?  
79 - safeExecute(settings.strokeOpacityFunction,  
80 - [this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.strokeOpacity,  
81 - weight: settings.useStrokeWeightFunction ?  
82 - safeExecute(settings.strokeWeightFunction,  
83 - [this.data, this.dataSources, this.dataSources[0]?.dsIndex]) : settings.strokeWeight,  
84 - } 73 + getPolyStyle(settings: PolylineSettings): L.PolylineOptions {
  74 + return {
  75 + interactive: false,
  76 + color: settings.useColorFunction ?
  77 + safeExecute(settings.colorFunction,
  78 + [this.data, this.dataSources, this.data.dsIndex]) : settings.color,
  79 + opacity: settings.useStrokeOpacityFunction ?
  80 + safeExecute(settings.strokeOpacityFunction,
  81 + [this.data, this.dataSources, this.data.dsIndex]) : settings.strokeOpacity,
  82 + weight: settings.useStrokeWeightFunction ?
  83 + safeExecute(settings.strokeWeightFunction,
  84 + [this.data, this.dataSources, this.data.dsIndex]) : settings.strokeWeight,
85 } 85 }
  86 + }
86 87
87 - removePolyline() {  
88 - this.map.removeLayer(this.leafletPoly);  
89 - } 88 + removePolyline() {
  89 + this.map.removeLayer(this.leafletPoly);
  90 + }
90 91
91 - getPolylineLatLngs() {  
92 - return this.leafletPoly.getLatLngs();  
93 - } 92 + getPolylineLatLngs() {
  93 + return this.leafletPoly.getLatLngs();
  94 + }
94 95
95 - setPolylineLatLngs(latLngs) {  
96 - this.leafletPoly.setLatLngs(latLngs);  
97 - } 96 + setPolylineLatLngs(latLngs) {
  97 + this.leafletPoly.setLatLngs(latLngs);
  98 + }
98 } 99 }
@@ -841,52 +841,57 @@ export const pathSchema = @@ -841,52 +841,57 @@ export const pathSchema =
841 }; 841 };
842 842
843 export const pointSchema = 843 export const pointSchema =
844 -{ 844 + {
845 schema: { 845 schema: {
846 - title: 'Trip Animation Path Configuration',  
847 - type: 'object',  
848 - properties: {  
849 - showPoints: {  
850 - title: 'Show points',  
851 - type: 'boolean',  
852 - default: false  
853 - },  
854 - pointColor: {  
855 - title: 'Point color',  
856 - type: 'string'  
857 - },  
858 - pointSize: {  
859 - title: 'Point size (px)',  
860 - type: 'number',  
861 - default: 10  
862 - },  
863 - usePointAsAnchor: {  
864 - title: 'Use point as anchor',  
865 - type: 'boolean',  
866 - default: false  
867 - },  
868 - pointAsAnchorFunction: {  
869 - title: 'Point as anchor function: f(data, dsData, dsIndex)',  
870 - type: 'string'  
871 - },  
872 - pointTooltipOnRightPanel: {  
873 - title: 'Independant point tooltip',  
874 - type: 'boolean',  
875 - default: true  
876 - }, 846 + title: 'Trip Animation Path Configuration',
  847 + type: 'object',
  848 + properties: {
  849 + showPoints: {
  850 + title: 'Show points',
  851 + type: 'boolean',
  852 + default: false
877 }, 853 },
878 - required: [] 854 + pointColor: {
  855 + title: 'Point color',
  856 + type: 'string'
  857 + },
  858 + pointSize: {
  859 + title: 'Point size (px)',
  860 + type: 'number',
  861 + default: 10
  862 + },
  863 + usePointAsAnchor: {
  864 + title: 'Use point as anchor',
  865 + type: 'boolean',
  866 + default: false
  867 + },
  868 + pointAsAnchorFunction: {
  869 + title: 'Point as anchor function: f(data, dsData, dsIndex)',
  870 + type: 'string'
  871 + },
  872 + pointTooltipOnRightPanel: {
  873 + title: 'Independant point tooltip',
  874 + type: 'boolean',
  875 + default: true
  876 + },
  877 + },
  878 + required: []
879 }, 879 },
880 form: [ 880 form: [
881 - 'showPoints', {  
882 - key: 'pointColor',  
883 - type: 'color'  
884 - }, 'pointSize', 'usePointAsAnchor', {  
885 - key: 'pointAsAnchorFunction',  
886 - type: 'javascript'  
887 - }, 'pointTooltipOnRightPanel', 881 + 'showPoints',
  882 + {
  883 + key: 'pointColor',
  884 + type: 'color'
  885 + },
  886 + 'pointSize',
  887 + 'usePointAsAnchor',
  888 + {
  889 + key: 'pointAsAnchorFunction',
  890 + type: 'javascript'
  891 + },
  892 + 'pointTooltipOnRightPanel',
888 ] 893 ]
889 -}; 894 + };
890 895
891 export const mapProviderSchema = 896 export const mapProviderSchema =
892 { 897 {
@@ -32,6 +32,12 @@ @@ -32,6 +32,12 @@
32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}"> 32 [ngStyle]="{'background-color': settings.tooltipColor, 'opacity': settings.tooltipOpacity, 'color': settings.tooltipFontColor}">
33 </div> 33 </div>
34 </div> 34 </div>
35 - <tb-history-selector *ngIf="historicalData" [settings]="settings" [intervals]="intervals" [anchors]="anchors" [useAnchors]="useAnchors"  
36 - (timeUpdated)="timeUpdated($event)"></tb-history-selector>  
37 -</div>  
  35 + <tb-history-selector *ngIf="historicalData"
  36 + [settings]="settings"
  37 + [minTime]="minTime"
  38 + [maxTime]="maxTime"
  39 + [step]="normalizationStep"
  40 + [anchors]="anchors"
  41 + [useAnchors]="useAnchors"
  42 + (timeUpdated)="timeUpdated($event)"></tb-history-selector>
  43 +</div>
@@ -14,21 +14,28 @@ @@ -14,21 +14,28 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import L from 'leaflet';  
18 import _ from 'lodash'; 17 import _ from 'lodash';
19 import tinycolor from 'tinycolor2'; 18 import tinycolor from 'tinycolor2';
20 -import { interpolateOnPointSegment } from 'leaflet-geometryutil';  
21 19
22 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core'; 20 import { AfterViewInit, ChangeDetectorRef, Component, Input, OnInit, SecurityContext, ViewChild } from '@angular/core';
23 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2'; 21 import { MapWidgetController, TbMapWidgetV2 } from '../lib/maps/map-widget2';
24 -import { MapProviders, FormattedData } from '../lib/maps/map-models';  
25 -import { initSchema, addToSchema, addGroupInfo, addCondition } from '@app/core/schema-utils';  
26 -import { tripAnimationSchema, mapPolygonSchema, pathSchema, pointSchema } from '../lib/maps/schemes'; 22 +import { FormattedData, MapProviders } from '../lib/maps/map-models';
  23 +import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
  24 +import { mapPolygonSchema, pathSchema, pointSchema, tripAnimationSchema } from '../lib/maps/schemes';
27 import { DomSanitizer } from '@angular/platform-browser'; 25 import { DomSanitizer } from '@angular/platform-browser';
28 import { WidgetContext } from '@app/modules/home/models/widget-component.models'; 26 import { WidgetContext } from '@app/modules/home/models/widget-component.models';
29 -import { findAngle, getRatio, parseArray, parseWithTranslation, safeExecute } from '../lib/maps/maps-utils'; 27 +import {
  28 + findAngle,
  29 + getRatio,
  30 + interpolateOnLineSegment,
  31 + parseArray,
  32 + parseFunction,
  33 + parseWithTranslation,
  34 + safeExecute
  35 +} from '../lib/maps/maps-utils';
30 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models'; 36 import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
31 import moment from 'moment'; 37 import moment from 'moment';
  38 +import { isUndefined } from '@core/utils';
32 39
33 40
34 @Component({ 41 @Component({
@@ -46,20 +53,22 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -46,20 +53,22 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
46 @ViewChild('map') mapContainer; 53 @ViewChild('map') mapContainer;
47 54
48 mapWidget: MapWidgetController; 55 mapWidget: MapWidgetController;
49 - historicalData; 56 + historicalData: FormattedData[][];
  57 + normalizationStep: number;
  58 + interpolatedTimeData = [];
50 intervals = []; 59 intervals = [];
51 - normalizationStep = 1000;  
52 - interpolatedData = [];  
53 widgetConfig: WidgetConfig; 60 widgetConfig: WidgetConfig;
54 settings; 61 settings;
55 mainTooltip = ''; 62 mainTooltip = '';
56 visibleTooltip = false; 63 visibleTooltip = false;
57 - activeTrip; 64 + activeTrip: FormattedData;
58 label; 65 label;
59 - minTime;  
60 - maxTime;  
61 - anchors = [];  
62 - useAnchors = false; 66 + minTime: number;
  67 + minTimeFormat: string;
  68 + maxTime: number;
  69 + maxTimeFormat: string;
  70 + anchors: number[] = [];
  71 + useAnchors: boolean;
63 72
64 static getSettingsSchema(): JsonSettingsSchema { 73 static getSettingsSchema(): JsonSettingsSchema {
65 const schema = initSchema(); 74 const schema = initSchema();
@@ -86,7 +95,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -86,7 +95,8 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
86 rotationAngle: 0 95 rotationAngle: 0
87 } 96 }
88 this.settings = { ...settings, ...this.ctx.settings }; 97 this.settings = { ...settings, ...this.ctx.settings };
89 - this.useAnchors = this.settings.usePointAsAnchor && this.settings.showPoints; 98 + this.useAnchors = this.settings.showPoints && this.settings.usePointAsAnchor;
  99 + this.settings.pointAsAnchorFunction = parseFunction(this.settings.pointAsAnchorFunction, ['data', 'dsData', 'dsIndex']);
90 this.settings.fitMapBounds = true; 100 this.settings.fitMapBounds = true;
91 this.normalizationStep = this.settings.normalizationStep; 101 this.normalizationStep = this.settings.normalizationStep;
92 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]]; 102 const subscription = this.ctx.subscriptions[Object.keys(this.ctx.subscriptions)[0]];
@@ -95,7 +105,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -95,7 +105,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
95 if (this.historicalData.length) { 105 if (this.historicalData.length) {
96 this.activeTrip = this.historicalData[0][0]; 106 this.activeTrip = this.historicalData[0][0];
97 this.calculateIntervals(); 107 this.calculateIntervals();
98 - this.timeUpdated(this.intervals[0]); 108 + this.timeUpdated(this.minTime);
99 } 109 }
100 this.mapWidget.map.map?.invalidateSize(); 110 this.mapWidget.map.map?.invalidateSize();
101 this.cd.detectChanges(); 111 this.cd.detectChanges();
@@ -108,30 +118,40 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -108,30 +118,40 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
108 } 118 }
109 119
110 timeUpdated(time: number) { 120 timeUpdated(time: number) {
111 - this.minTime = moment(this.intervals[this.intervals.length - 1]).format('YYYY-MM-DD HH:mm:ss');  
112 - this.maxTime = moment(this.intervals[0]).format('YYYY-MM-DD HH:mm:ss');  
113 - const currentPosition = this.interpolatedData 121 + const currentPosition = this.interpolatedTimeData
114 .map(dataSource => dataSource[time]) 122 .map(dataSource => dataSource[time])
115 .filter(ds => ds) 123 .filter(ds => ds)
116 .map(ds => { 124 .map(ds => {
117 - ds.minTime = this.minTime;  
118 - ds.maxTime = this.maxTime; 125 + ds.minTime = this.minTimeFormat;
  126 + ds.maxTime = this.maxTimeFormat;
119 return ds; 127 return ds;
120 }); 128 });
  129 + if (isUndefined(currentPosition[0])) {
  130 + const timePoints = Object.keys(this.interpolatedTimeData[0]).map(item => parseInt(item, 10));
  131 + for (let i = 1; i < timePoints.length; i++) {
  132 + if (timePoints[i - 1] < time && timePoints[i] > time) {
  133 + const beforePosition = this.interpolatedTimeData[0][timePoints[i - 1]];
  134 + const afterPosition = this.interpolatedTimeData[0][timePoints[i]];
  135 + const ratio = getRatio(timePoints[i - 1], timePoints[i], time);
  136 + currentPosition[0] = {
  137 + ...beforePosition,
  138 + time,
  139 + ...interpolateOnLineSegment(beforePosition, afterPosition, this.settings.latKeyName, this.settings.lngKeyName, ratio)
  140 + }
  141 + break;
  142 + }
  143 + }
  144 + }
121 this.activeTrip = currentPosition[0]; 145 this.activeTrip = currentPosition[0];
122 this.calcLabel(); 146 this.calcLabel();
123 this.calcTooltip(); 147 this.calcTooltip();
124 if (this.mapWidget) { 148 if (this.mapWidget) {
125 - this.mapWidget.map.updatePolylines(this.interpolatedData.map(ds => _.values(ds))); 149 + this.mapWidget.map.updatePolylines(this.interpolatedTimeData.map(ds => _.values(ds)), this.activeTrip);
126 if (this.settings.showPolygon) { 150 if (this.settings.showPolygon) {
127 - this.mapWidget.map.updatePolygons(this.interpolatedData); 151 + this.mapWidget.map.updatePolygons(this.interpolatedTimeData);
128 } 152 }
129 if (this.settings.showPoints) { 153 if (this.settings.showPoints) {
130 - this.mapWidget.map.updatePoints(this.historicalData[0], this.calcTooltip);  
131 - this.anchors = this.historicalData[0]  
132 - .filter(data =>  
133 - this.settings.usePointAsAnchor ||  
134 - safeExecute(this.settings.pointAsAnchorFunction, [this.historicalData, data, data.dsIndex])).map(data => data.time); 154 + this.mapWidget.map.updatePoints(_.values(_.union(this.interpolatedTimeData)[0]), this.calcTooltip);
135 } 155 }
136 this.mapWidget.map.updateMarkers(currentPosition, this.calcTooltip); 156 this.mapWidget.map.updateMarkers(currentPosition, this.calcTooltip);
137 } 157 }
@@ -142,22 +162,31 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -142,22 +162,31 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
142 162
143 calculateIntervals() { 163 calculateIntervals() {
144 this.historicalData.forEach((dataSource, index) => { 164 this.historicalData.forEach((dataSource, index) => {
145 - this.intervals = [];  
146 - for (let time = dataSource[0]?.time; time < dataSource[dataSource.length - 1]?.time; time += this.normalizationStep) {  
147 - this.intervals.push(time);  
148 - }  
149 - this.intervals.push(dataSource[dataSource.length - 1]?.time);  
150 - this.interpolatedData[index] = this.interpolateArray(dataSource, this.intervals); 165 + this.minTime = dataSource[0]?.time || Infinity;
  166 + this.minTimeFormat = this.minTime !== Infinity ? moment(this.minTime).format('YYYY-MM-DD HH:mm:ss') : '';
  167 + this.maxTime = dataSource[dataSource.length - 1]?.time || -Infinity;
  168 + this.maxTimeFormat = this.maxTime !== -Infinity ? moment(this.maxTime).format('YYYY-MM-DD HH:mm:ss') : '';
  169 + this.interpolatedTimeData[index] = this.interpolateArray(dataSource);
151 }); 170 });
  171 + if (this.useAnchors) {
  172 + const anchorDate = Object.entries(_.union(this.interpolatedTimeData)[0]);
  173 + this.anchors = anchorDate
  174 + .filter((data: [string, FormattedData]) => safeExecute(this.settings.pointAsAnchorFunction, [data[1], anchorDate, data[1].dsIndex]))
  175 + .map(data => parseInt(data[0], 10));
  176 + }
152 } 177 }
153 178
154 calcTooltip = (point?: FormattedData, setTooltip = true) => { 179 calcTooltip = (point?: FormattedData, setTooltip = true) => {
155 if (!point) { 180 if (!point) {
156 point = this.activeTrip; 181 point = this.activeTrip;
157 } 182 }
158 - const data = { ...point, maxTime: this.maxTime, minTime: this.minTime } 183 + const data = {
  184 + ...this.activeTrip,
  185 + maxTime: this.maxTimeFormat,
  186 + minTime: this.minTimeFormat
  187 + }
159 const tooltipPattern: string = this.settings.useTooltipFunction ? 188 const tooltipPattern: string = this.settings.useTooltipFunction ?
160 - safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, 0]) : this.settings.tooltipPattern; 189 + safeExecute(this.settings.tooolTipFunction, [data, this.historicalData, point.dsIndex]) : this.settings.tooltipPattern;
161 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true); 190 const tooltipText = parseWithTranslation.parseTemplate(tooltipPattern, data, true);
162 if (setTooltip) { 191 if (setTooltip) {
163 this.mainTooltip = this.sanitizer.sanitize( 192 this.mainTooltip = this.sanitizer.sanitize(
@@ -168,34 +197,34 @@ export class TripAnimationComponent implements OnInit, AfterViewInit { @@ -168,34 +197,34 @@ export class TripAnimationComponent implements OnInit, AfterViewInit {
168 } 197 }
169 198
170 calcLabel() { 199 calcLabel() {
171 - const data = { ...this.activeTrip, maxTime: this.maxTime, minTime: this.minTime } 200 + const data = {
  201 + ...this.activeTrip,
  202 + maxTime: this.maxTimeFormat,
  203 + minTime: this.minTimeFormat
  204 + }
172 const labelText: string = this.settings.useLabelFunction ? 205 const labelText: string = this.settings.useLabelFunction ?
173 - safeExecute(this.settings.labelFunction, [data, this.historicalData, 0]) : this.settings.label; 206 + safeExecute(this.settings.labelFunction, [data, this.historicalData, data.dsIndex]) : this.settings.label;
174 this.label = (parseWithTranslation.parseTemplate(labelText, data, true)); 207 this.label = (parseWithTranslation.parseTemplate(labelText, data, true));
175 } 208 }
176 209
177 - interpolateArray(originData, interpolatedIntervals) { 210 + interpolateArray(originData: FormattedData[]) {
178 const result = {}; 211 const result = {};
179 - for (let i = 1, j = 0; i < originData.length && j < interpolatedIntervals.length;) {  
180 - const currentTime = interpolatedIntervals[j];  
181 - while (originData[i].time < currentTime) i++;  
182 - const before = originData[i - 1];  
183 - const after = originData[i];  
184 - const interpolation = interpolateOnPointSegment(  
185 - new L.Point(before.latitude, before.longitude),  
186 - new L.Point(after.latitude, after.longitude),  
187 - getRatio(before.time, after.time, currentTime));  
188 - result[currentTime] = ({  
189 - ...originData[i],  
190 - rotationAngle: findAngle(before, after) + this.settings.rotationAngle,  
191 - latitude: interpolation.x,  
192 - longitude: interpolation.y  
193 - });  
194 - j++; 212 + const latKeyName = this.settings.latKeyName;
  213 + const lngKeyName = this.settings.lngKeyName;
  214 + for (const data of originData) {
  215 + const currentTime = data.time;
  216 + const normalizeTime = this.minTime + Math.ceil((currentTime - this.minTime) / this.normalizationStep) * this.normalizationStep;
  217 + result[normalizeTime] = {
  218 + ...data,
  219 + rotationAngle: this.settings.rotationAngle
  220 + };
  221 + }
  222 + const timeStamp = Object.keys(result);
  223 + for (let i = 0; i < timeStamp.length - 1; i++) {
  224 + result[timeStamp[i]].rotationAngle += findAngle(result[timeStamp[i]], result[timeStamp[i + 1]], latKeyName, lngKeyName)
195 } 225 }
196 return result; 226 return result;
197 } 227 }
198 } 228 }
199 229
200 export let TbTripAnimationWidget = TripAnimationComponent; 230 export let TbTripAnimationWidget = TripAnimationComponent;
201 -  
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 tb-fullscreen 19 tb-fullscreen
20 [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column"> 20 [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
21 <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" class="tb-json-content-toolbar"> 21 <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" class="tb-json-content-toolbar">
22 - <label class="tb-title no-padding">{{ label }}</label> 22 + <label class="tb-title no-padding" [ngClass]="{'tb-error': !contentValid}">{{ label }}</label>
23 <span fxFlex></span> 23 <span fxFlex></span>
24 <button type="button" 24 <button type="button"
25 mat-button *ngIf="!readonly && !disabled" class="tidy" (click)="beautifyJSON()"> 25 mat-button *ngIf="!readonly && !disabled" class="tidy" (click)="beautifyJSON()">
@@ -92,6 +92,15 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid @@ -92,6 +92,15 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid
92 this.validateContentValue = coerceBooleanProperty(value); 92 this.validateContentValue = coerceBooleanProperty(value);
93 } 93 }
94 94
  95 + private validateOnChangeValue: boolean;
  96 + get validateOnChange(): boolean {
  97 + return this.validateOnChangeValue;
  98 + }
  99 + @Input()
  100 + set validateOnChange(value: boolean) {
  101 + this.validateOnChangeValue = coerceBooleanProperty(value);
  102 + }
  103 +
95 fullscreen = false; 104 fullscreen = false;
96 105
97 contentBody: string; 106 contentBody: string;
@@ -256,7 +265,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid @@ -256,7 +265,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid
256 const editorValue = this.jsonEditor.getValue(); 265 const editorValue = this.jsonEditor.getValue();
257 if (this.contentBody !== editorValue) { 266 if (this.contentBody !== editorValue) {
258 this.contentBody = editorValue; 267 this.contentBody = editorValue;
259 - this.contentValid = true; 268 + this.contentValid = !this.validateOnChange || this.doValidate();
260 this.propagateChange(this.contentBody); 269 this.propagateChange(this.contentBody);
261 } 270 }
262 } 271 }
@@ -117,6 +117,11 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc @@ -117,6 +117,11 @@ export class KeyValMapComponent extends PageComponent implements ControlValueAcc
117 this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => { 117 this.valueChangeSubscription = this.kvListFormGroup.valueChanges.subscribe(() => {
118 this.updateModel(); 118 this.updateModel();
119 }); 119 });
  120 + if (this.disabled) {
  121 + this.kvListFormGroup.disable({emitEvent: false});
  122 + } else {
  123 + this.kvListFormGroup.enable({emitEvent: false});
  124 + }
120 } 125 }
121 126
122 public removeKeyVal(index: number) { 127 public removeKeyVal(index: number) {
@@ -27,8 +27,8 @@ @@ -27,8 +27,8 @@
27 <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()"> 27 <mat-slider [(ngModel)]="index" [min]="minTimeIndex" [max]="maxTimeIndex" (change)="changeIndex()">
28 </mat-slider> 28 </mat-slider>
29 <div class="panel-timer"> 29 <div class="panel-timer">
30 - <span *ngIf="this.intervals[this.index]">{{ this.intervals[this.index] | date:'medium'}}</span>  
31 - <span *ngIf="!this.intervals[this.index]">{{ "widget.no-data-found" | translate}}</span> 30 + <span *ngIf="this.currentTime">{{ this.currentTime | date:'medium'}}</span>
  31 + <span *ngIf="!this.currentTime">{{ "widget.no-data-found" | translate}}</span>
32 </div> 32 </div>
33 </div> 33 </div>
34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()"> 34 <button mat-icon-button class="mat-icon-button" aria-label="Next" (click)="moveNext()">
@@ -47,8 +47,9 @@ @@ -47,8 +47,9 @@
47 pause_circle_outline 47 pause_circle_outline
48 </mat-icon> 48 </mat-icon>
49 </button> 49 </button>
50 - <mat-select matInput [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select" 50 + <mat-select [(ngModel)]="speed" (selectionChange)="reeneble()" class="speed-select"
51 aria-label="Speed selector"> 51 aria-label="Speed selector">
52 <mat-option [value]="speedValue" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option> 52 <mat-option [value]="speedValue" *ngFor="let speedValue of speeds">{{speedValue}} </mat-option>
53 </mat-select> 53 </mat-select>
54 - </div>  
  54 + </div>
  55 +</div>
@@ -126,7 +126,7 @@ @@ -126,7 +126,7 @@
126 } 126 }
127 127
128 .speed-select { 128 .speed-select {
129 - width: 50px; 129 + width: 70px;
130 margin-left: 10px; 130 margin-left: 10px;
131 margin-top: 10px; 131 margin-top: 10px;
132 } 132 }
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { Component, OnInit, OnChanges, Input, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; 17 +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
18 import { interval } from 'rxjs'; 18 import { interval } from 'rxjs';
19 import { filter } from 'rxjs/operators'; 19 import { filter } from 'rxjs/operators';
20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models'; 20 import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/maps/map-models';
@@ -27,13 +27,14 @@ import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/m @@ -27,13 +27,14 @@ import { HistorySelectSettings } from '@app/modules/home/components/widget/lib/m
27 export class HistorySelectorComponent implements OnInit, OnChanges { 27 export class HistorySelectorComponent implements OnInit, OnChanges {
28 28
29 @Input() settings: HistorySelectSettings 29 @Input() settings: HistorySelectSettings
30 - @Input() intervals = []; 30 + @Input() minTime: number;
  31 + @Input() maxTime: number;
  32 + @Input() step = 1000;
31 @Input() anchors = []; 33 @Input() anchors = [];
32 @Input() useAnchors = false; 34 @Input() useAnchors = false;
33 35
34 @Output() timeUpdated: EventEmitter<number> = new EventEmitter(); 36 @Output() timeUpdated: EventEmitter<number> = new EventEmitter();
35 37
36 - animationTime;  
37 minTimeIndex = 0; 38 minTimeIndex = 0;
38 maxTimeIndex = 0; 39 maxTimeIndex = 0;
39 speed = 1; 40 speed = 1;
@@ -41,6 +42,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -41,6 +42,7 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
41 playing = false; 42 playing = false;
42 interval; 43 interval;
43 speeds = [1, 5, 10, 25]; 44 speeds = [1, 5, 10, 25];
  45 + currentTime = null;
44 46
45 47
46 constructor(private cd: ChangeDetectorRef) { } 48 constructor(private cd: ChangeDetectorRef) { }
@@ -49,7 +51,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -49,7 +51,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
49 } 51 }
50 52
51 ngOnChanges() { 53 ngOnChanges() {
52 - this.maxTimeIndex = this.intervals?.length - 1; 54 + this.maxTimeIndex = Math.ceil((this.maxTime - this.minTime) / this.step);
  55 + this.currentTime = this.minTime === Infinity ? null : this.minTime;
53 } 56 }
54 57
55 play() { 58 play() {
@@ -59,17 +62,18 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -59,17 +62,18 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
59 .pipe( 62 .pipe(
60 filter(() => this.playing)).subscribe(() => { 63 filter(() => this.playing)).subscribe(() => {
61 this.index++; 64 this.index++;
62 - if (this.index < this.maxTimeIndex) { 65 + this.currentTime = this.minTime + this.index * this.step;
  66 + if (this.index <= this.maxTimeIndex) {
63 this.cd.detectChanges(); 67 this.cd.detectChanges();
64 - this.timeUpdated.emit(this.intervals[this.index]); 68 + this.timeUpdated.emit(this.currentTime);
65 } 69 }
66 else { 70 else {
67 this.interval.complete(); 71 this.interval.complete();
68 } 72 }
69 }, err => { 73 }, err => {
70 - console.log(err); 74 + console.error(err);
71 }, () => { 75 }, () => {
72 - this.index = this.minTimeIndex; 76 + this.currentTime = this.index = this.minTimeIndex;
73 this.playing = false; 77 this.playing = false;
74 this.interval = null; 78 this.interval = null;
75 this.cd.detectChanges(); 79 this.cd.detectChanges();
@@ -87,18 +91,19 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -87,18 +91,19 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
87 91
88 pause() { 92 pause() {
89 this.playing = false; 93 this.playing = false;
  94 + this.currentTime = this.minTime + this.index * this.step;
90 this.cd.detectChanges(); 95 this.cd.detectChanges();
91 - this.timeUpdated.emit(this.intervals[this.index]); 96 + this.timeUpdated.emit(this.currentTime);
92 } 97 }
93 98
94 moveNext() { 99 moveNext() {
95 if (this.index < this.maxTimeIndex) { 100 if (this.index < this.maxTimeIndex) {
96 if (this.useAnchors) { 101 if (this.useAnchors) {
97 - const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors)+1;  
98 - this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);  
99 - }  
100 - else 102 + const anchorIndex = this.findIndex(this.currentTime, this.anchors) + 1;
  103 + this.index = Math.floor((this.anchors[anchorIndex] - this.minTime) / this.step);
  104 + } else {
101 this.index++; 105 this.index++;
  106 + }
102 } 107 }
103 this.pause(); 108 this.pause();
104 } 109 }
@@ -106,15 +111,23 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -106,15 +111,23 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
106 movePrev() { 111 movePrev() {
107 if (this.index > this.minTimeIndex) { 112 if (this.index > this.minTimeIndex) {
108 if (this.useAnchors) { 113 if (this.useAnchors) {
109 - const anchorIndex = this.findIndex(this.intervals[this.index], this.anchors) - 1;  
110 - this.index = this.findIndex(this.anchors[anchorIndex], this.intervals);  
111 - }  
112 - else 114 + const anchorIndex = this.findIndex(this.currentTime, this.anchors) - 1;
  115 + this.index = Math.floor((this.anchors[anchorIndex] - this.minTime) / this.step);
  116 + } else {
113 this.index--; 117 this.index--;
  118 + }
114 } 119 }
115 this.pause(); 120 this.pause();
116 } 121 }
117 122
  123 + findIndex(value: number, array: number[]): number {
  124 + let i = 0;
  125 + while (array[i] < value) {
  126 + i++;
  127 + }
  128 + return i;
  129 + }
  130 +
118 moveStart() { 131 moveStart() {
119 this.index = this.minTimeIndex; 132 this.index = this.minTimeIndex;
120 this.pause(); 133 this.pause();
@@ -125,15 +138,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges { @@ -125,15 +138,8 @@ export class HistorySelectorComponent implements OnInit, OnChanges {
125 this.pause(); 138 this.pause();
126 } 139 }
127 140
128 - findIndex(value, array: any[]) {  
129 - let i = 0;  
130 - while (array[i] < value) {  
131 - i++;  
132 - };  
133 - return i;  
134 - }  
135 -  
136 changeIndex() { 141 changeIndex() {
137 - this.timeUpdated.emit(this.intervals[this.index]); 142 + this.currentTime = this.minTime + this.index * this.step;
  143 + this.timeUpdated.emit(this.currentTime);
138 } 144 }
139 } 145 }
@@ -90,6 +90,14 @@ @@ -90,6 +90,14 @@
90 "timeout-invalid": "That doesn't look like a valid timeout.", 90 "timeout-invalid": "That doesn't look like a valid timeout.",
91 "enable-tls": "Enable TLS", 91 "enable-tls": "Enable TLS",
92 "tls-version": "TLS version", 92 "tls-version": "TLS version",
  93 + "enable-proxy": "Enable proxy",
  94 + "proxy-host": "Proxy host",
  95 + "proxy-host-required": "Proxy host is required.",
  96 + "proxy-port": "Proxy port",
  97 + "proxy-port-required": "You must supply a proxy port.",
  98 + "proxy-port-invalid": "That doesn't look like a valid proxy port.",
  99 + "proxy-user": "Proxy user",
  100 + "proxy-password": "Proxy password",
93 "send-test-mail": "Send test mail", 101 "send-test-mail": "Send test mail",
94 "security-settings": "Security settings", 102 "security-settings": "Security settings",
95 "password-policy": "Password policy", 103 "password-policy": "Password policy",