thingsboard-developers
Changes
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java 6(+3 -3)
application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java 2(+1 -1)
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java 6(+6 -0)
dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java 1(+1 -0)
extensions/extension-sns/pom.xml 81(+81 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionMsg.java 31(+31 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionPayload.java 37(+37 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginAction.java 45(+45 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginActionConfiguration.java 30(+30 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsMessageHandler.java 63(+63 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPlugin.java 79(+79 -0)
extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java 30(+30 -0)
extensions/extention-sqs/pom.xml 81(+81 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionMsg.java 31(+31 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionPayload.java 39(+39 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginAction.java 49(+49 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginActionConfiguration.java 30(+30 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionMsg.java 31(+31 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionPayload.java 39(+39 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginAction.java 46(+46 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginActionConfiguration.java 32(+32 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsMessageHandler.java 84(+84 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPlugin.java 78(+78 -0)
extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPluginConfiguration.java 30(+30 -0)
extensions/extention-sqs/src/test/java/org/thingsboard/server/extensions/sqs/SqsDemoClient.java 69(+69 -0)
extensions/pom.xml 2(+2 -0)
extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java 8(+6 -2)
pom.xml 12(+12 -0)
resume.bat 18(+18 -0)
tools/src/main/python/mqtt-send-telemetry.py 36(+36 -0)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java 44(+25 -19)
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java 22(+14 -8)
ui/src/app/api/login.service.js 16(+8 -8)
ui/src/app/api/subscription.js 3(+2 -1)
ui/src/app/api/user.service.js 19(+10 -9)
ui/src/app/components/js-func.scss 2(+1 -1)
ui/src/app/home/home-links.scss 26(+26 -0)
ui/src/app/home/home-links.tpl.html 2(+1 -1)
ui/src/scss/main.scss 6(+5 -1)
Details
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index 00af046..14ff096 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -164,7 +164,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
} else {
logger.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
}
- Set<UUID> sentOneWayIds = new HashSet<>();
+ Set<Integer> sentOneWayIds = new HashSet<>();
if (type == SessionType.ASYNC) {
rpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, server, sentOneWayIds));
} else {
@@ -174,12 +174,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
sentOneWayIds.forEach(rpcPendingMap::remove);
}
- private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<UUID> sentOneWayIds) {
+ private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(ActorContext context, SessionId sessionId, Optional<ServerAddress> server, Set<Integer> sentOneWayIds) {
return entry -> {
ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
ToDeviceRpcRequestBody body = request.getBody();
if (request.isOneway()) {
- sentOneWayIds.add(request.getId());
+ sentOneWayIds.add(entry.getKey());
ToPluginRpcResponseDeviceMsg responsePluginMsg = toPluginRpcResponseMsg(entry.getValue().getMsg(), (String) null);
context.parent().tell(responsePluginMsg, ActorRef.noSender());
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
index c695ec8..7a7e3ff 100644
--- a/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
@@ -170,7 +170,7 @@ class RuleActorMessageProcessor extends ComponentMsgProcessor<RuleId> {
Optional<RuleToPluginMsg<?>> ruleToPluginMsgOptional = action.convert(ruleCtx, inMsg, inMsgMd);
if (ruleToPluginMsgOptional.isPresent()) {
RuleToPluginMsg<?> ruleToPluginMsg = ruleToPluginMsgOptional.get();
- logger.debug("[{}] Device msg is converter to: {}", entityId, ruleToPluginMsg);
+ logger.debug("[{}] Device msg is converted to: {}", entityId, ruleToPluginMsg);
context.parent().tell(new RuleToPluginMsgWrapper(pluginTenantId, pluginId, tenantId, entityId, ruleToPluginMsg), context.self());
if (action.isOneWayAction()) {
pushToNextRule(context, msg.getCtx(), RuleEngineError.NO_TWO_WAY_ACTIONS);
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 6395798..6755408 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -28,6 +28,7 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -148,6 +149,11 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
}
@Override
+ public void configure(WebSecurity web) throws Exception {
+ web.ignoring().antMatchers("/static/**");
+ }
+
+ @Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().cacheControl().and().frameOptions().disable()
.and()
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
index 0246e07..cb2f5b9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
@@ -19,8 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -30,7 +28,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.security.UserCredentials;
-import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.mail.MailService;
@@ -78,9 +75,10 @@ public class AuthController extends BaseController {
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void changePassword (
- @RequestParam(value = "currentPassword") String currentPassword,
- @RequestParam(value = "newPassword") String newPassword) throws ThingsboardException {
+ @RequestBody JsonNode changePasswordRequest) throws ThingsboardException {
try {
+ String currentPassword = changePasswordRequest.get("currentPassword").asText();
+ String newPassword = changePasswordRequest.get("newPassword").asText();
SecurityUser securityUser = getCurrentUser();
UserCredentials userCredentials = userService.findUserCredentialsByUserId(securityUser.getId());
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
@@ -118,9 +116,10 @@ public class AuthController extends BaseController {
@RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void requestResetPasswordByEmail (
- @RequestParam(value = "email") String email,
+ @RequestBody JsonNode resetPasswordByEmailRequest,
HttpServletRequest request) throws ThingsboardException {
try {
+ String email = resetPasswordByEmailRequest.get("email").asText();
UserCredentials userCredentials = userService.requestPasswordReset(email);
String baseUrl = constructBaseUrl(request);
String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
@@ -158,10 +157,11 @@ public class AuthController extends BaseController {
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode activateUser(
- @RequestParam(value = "activateToken") String activateToken,
- @RequestParam(value = "password") String password,
+ @RequestBody JsonNode activateRequest,
HttpServletRequest request) throws ThingsboardException {
try {
+ String activateToken = activateRequest.get("activateToken").asText();
+ String password = activateRequest.get("password").asText();
String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(activateToken, encodedPassword);
User user = userService.findUserById(credentials.getUserId());
@@ -194,10 +194,11 @@ public class AuthController extends BaseController {
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JsonNode resetPassword(
- @RequestParam(value = "resetToken") String resetToken,
- @RequestParam(value = "password") String password,
+ @RequestBody JsonNode resetPasswordRequest,
HttpServletRequest request) throws ThingsboardException {
try {
+ String resetToken = resetPasswordRequest.get("resetToken").asText();
+ String password = resetPasswordRequest.get("password").asText();
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(resetToken);
if (userCredentials != null) {
String encodedPassword = passwordEncoder.encode(password);
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 657e28e..44319c6 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -66,8 +66,8 @@ plugins:
# JWT Token parameters
security.jwt:
- tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000000}" # Number of seconds (15 mins)
- refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:36000000}" # Seconds (1 hour)
+ tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:900}" # Number of seconds (15 mins)
+ refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:3600}" # Seconds (1 hour)
tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}"
tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}"
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
index 689f316..19e4329 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -221,7 +221,10 @@ public abstract class AbstractControllerTest {
doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
.andExpect(status().isSeeOther())
.andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
- JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", "activateToken", TestMailService.currentActivateToken, "password", password).andExpect(status().isOk()), JsonNode.class);
+ JsonNode activateRequest = new ObjectMapper().createObjectNode()
+ .put("activateToken", TestMailService.currentActivateToken)
+ .put("password", password);
+ JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
validateAndSetJwtToken(tokenInfo, user.getEmail());
return savedUser;
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
index 8dac038..e3c87f6 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -73,7 +74,11 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isSeeOther())
.andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
- JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", "activateToken", TestMailService.currentActivateToken, "password", "testPassword").andExpect(status().isOk()), JsonNode.class);
+ JsonNode activateRequest = new ObjectMapper().createObjectNode()
+ .put("activateToken", TestMailService.currentActivateToken)
+ .put("password", "testPassword");
+
+ JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user")
@@ -117,13 +122,21 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User savedUser = createUserAndLogin(user, "testPassword1");
logout();
- doPost("/api/noauth/resetPasswordByEmail", "email", email)
+
+ JsonNode resetPasswordByEmailRequest = new ObjectMapper().createObjectNode()
+ .put("email", email);
+
+ doPost("/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest)
.andExpect(status().isOk());
doGet("/api/noauth/resetPassword?resetToken={resetToken}", TestMailService.currentResetPasswordToken)
.andExpect(status().isSeeOther())
.andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
-
- JsonNode tokenInfo = readResponse(doPost("/api/noauth/resetPassword", "resetToken", TestMailService.currentResetPasswordToken, "password", "testPassword2").andExpect(status().isOk()), JsonNode.class);
+
+ JsonNode resetPasswordRequest = new ObjectMapper().createObjectNode()
+ .put("resetToken", TestMailService.currentResetPasswordToken)
+ .put("password", "testPassword2");
+
+ JsonNode tokenInfo = readResponse(doPost("/api/noauth/resetPassword", resetPasswordRequest).andExpect(status().isOk()), JsonNode.class);
validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user")
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java
index 7c34aa1..7828267 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java
@@ -22,6 +22,7 @@ import javax.annotation.PreDestroy;
import java.util.concurrent.Executors;
public abstract class JpaAbstractDaoListeningExecutorService {
+
protected ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
@PreDestroy
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
index 7fddfae..170f9a6 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
@@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
+import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
@@ -36,10 +34,12 @@ import org.thingsboard.server.dao.timeseries.TimeseriesDao;
import org.thingsboard.server.dao.util.SqlDao;
import javax.annotation.Nullable;
+import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
@@ -50,6 +50,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
@SqlDao
public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao {
+ private ListeningExecutorService insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
+
@Autowired
private TsKvRepository tsKvRepository;
@@ -232,7 +234,8 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
- return service.submit(() -> {
+ log.trace("Saving entity: " + entity);
+ return insertService.submit(() -> {
tsKvRepository.save(entity);
return null;
});
@@ -240,7 +243,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
@Override
public ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) {
- return service.submit(() -> null);
+ return insertService.submit(() -> null);
}
@Override
@@ -254,10 +257,15 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp
latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));
latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
- return service.submit(() -> {
+ return insertService.submit(() -> {
tsKvLatestRepository.save(latestEntity);
return null;
});
}
+ @PreDestroy
+ void onDestroy() {
+ insertService.shutdown();
+ }
+
}
extensions/extension-sns/pom.xml 81(+81 -0)
diff --git a/extensions/extension-sns/pom.xml b/extensions/extension-sns/pom.xml
new file mode 100644
index 0000000..c68ea7d
--- /dev/null
+++ b/extensions/extension-sns/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright © 2016-2017 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>extensions</artifactId>
+ <groupId>org.thingsboard</groupId>
+ <version>1.4.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.thingsboard.extensions</groupId>
+ <artifactId>extension-sns</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard Server SNS Extension</name>
+ <url>http://thingsboard.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../..</main.dir>
+ <aws.sdk.version>1.11.229</aws.sdk.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard</groupId>
+ <artifactId>extensions-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard</groupId>
+ <artifactId>extensions-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk-sns</artifactId>
+ <version>${aws.sdk.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assembly/extension.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/extensions/extension-sns/src/assembly/extension.xml b/extensions/extension-sns/src/assembly/extension.xml
new file mode 100644
index 0000000..2395d55
--- /dev/null
+++ b/extensions/extension-sns/src/assembly/extension.xml
@@ -0,0 +1,37 @@
+<!--
+
+ Copyright © 2016-2017 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
+ <id>extension</id>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>/</outputDirectory>
+ <useProjectArtifact>true</useProjectArtifact>
+ <unpack>true</unpack>
+ <scope>runtime</scope>
+ <excludes>
+
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionMsg.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionMsg.java
new file mode 100644
index 0000000..b64a22b
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionMsg.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.action;
+
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+public class SnsTopicActionMsg extends AbstractRuleToPluginMsg<SnsTopicActionPayload> {
+
+ public SnsTopicActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, SnsTopicActionPayload payload) {
+ super(tenantId, customerId, deviceId, payload);
+ }
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionPayload.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionPayload.java
new file mode 100644
index 0000000..a1bf89a
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionPayload.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.action;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.msg.session.MsgType;
+
+import java.io.Serializable;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+@Data
+@Builder
+public class SnsTopicActionPayload implements Serializable {
+
+ private final String topicArn;
+ private final String msgBody;
+
+ private final Integer requestId;
+ private final MsgType msgType;
+ private final boolean sync;
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginAction.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginAction.java
new file mode 100644
index 0000000..79de23a
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginAction.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.action;
+
+import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
+import org.thingsboard.server.extensions.api.component.Action;
+import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
+import org.thingsboard.server.extensions.api.rules.RuleContext;
+import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction;
+
+import java.util.Optional;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+@Action(name = "SNS Topic Action", descriptor = "SnsTopicActionDescriptor.json", configuration = SnsTopicPluginActionConfiguration.class)
+public class SnsTopicPluginAction extends AbstractTemplatePluginAction<SnsTopicPluginActionConfiguration> {
+
+ @Override
+ protected Optional<RuleToPluginMsg> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) {
+ SnsTopicActionPayload.SnsTopicActionPayloadBuilder builder = SnsTopicActionPayload.builder();
+ builder.msgType(payload.getMsgType());
+ builder.requestId(payload.getRequestId());
+ builder.topicArn(configuration.getTopicArn());
+ builder.msgBody(getMsgBody(ctx, msg));
+ return Optional.of(new SnsTopicActionMsg(msg.getTenantId(),
+ msg.getCustomerId(),
+ msg.getDeviceId(),
+ builder.build()));
+ }
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginActionConfiguration.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginActionConfiguration.java
new file mode 100644
index 0000000..468c3dd
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginActionConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.action;
+
+import lombok.Data;
+import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+@Data
+public class SnsTopicPluginActionConfiguration implements TemplateActionConfiguration {
+
+ private String topicArn;
+ private String template;
+ private boolean sync;
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsMessageHandler.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsMessageHandler.java
new file mode 100644
index 0000000..c7fd99c
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsMessageHandler.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.plugin;
+
+import com.amazonaws.services.sns.AmazonSNS;
+import com.amazonaws.services.sns.model.PublishRequest;
+import com.amazonaws.services.sns.model.PublishResult;
+import com.amazonaws.services.sqs.AmazonSQS;
+import com.amazonaws.services.sqs.model.SendMessageRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
+import org.thingsboard.server.extensions.api.plugins.PluginContext;
+import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
+import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;
+import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
+import org.thingsboard.server.extensions.api.rules.RuleException;
+import org.thingsboard.server.extensions.sns.action.SnsTopicActionMsg;
+import org.thingsboard.server.extensions.sns.action.SnsTopicActionPayload;
+
+/**
+ * Created by Valerii Sosliuk on 11/6/2017.
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class SnsMessageHandler implements RuleMsgHandler {
+
+ private final AmazonSNS sns;
+
+ @Override
+ public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException {
+ if (msg instanceof SnsTopicActionMsg) {
+ SnsTopicActionPayload payload = ((SnsTopicActionMsg) msg).getPayload();
+ PublishRequest publishRequest = new PublishRequest()
+ .withTopicArn(payload.getTopicArn())
+ .withMessage(payload.getMsgBody());
+ sns.publish(publishRequest);
+ if (payload.isSync()) {
+ ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId,
+ BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId())));
+ }
+ return;
+ }
+ throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!");
+
+ }
+
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPlugin.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPlugin.java
new file mode 100644
index 0000000..86f5de0
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPlugin.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.plugin;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.sns.AmazonSNS;
+import com.amazonaws.services.sns.AmazonSNSClient;
+import org.thingsboard.server.extensions.api.component.Plugin;
+import org.thingsboard.server.extensions.api.plugins.AbstractPlugin;
+import org.thingsboard.server.extensions.api.plugins.PluginContext;
+import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
+import org.thingsboard.server.extensions.sns.action.SnsTopicPluginAction;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+@Plugin(name = "SNS Plugin", actions = {SnsTopicPluginAction.class},
+ descriptor = "SnsPluginDescriptor.json", configuration = SnsPluginConfiguration.class)
+public class SnsPlugin extends AbstractPlugin<SnsPluginConfiguration> {
+
+ private SnsMessageHandler snsMessageHandler;
+ private SnsPluginConfiguration configuration;
+
+ @Override
+ public void init(SnsPluginConfiguration configuration) {
+ this.configuration = configuration;
+ init();
+ }
+
+ private void init() {
+ AWSCredentials awsCredentials = new BasicAWSCredentials(configuration.getAccessKeyId(), configuration.getSecretAccessKey());
+ AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials);
+ AmazonSNS sns = AmazonSNSClient.builder()
+ .withCredentials(credProvider)
+ .withRegion(configuration.getRegion())
+ .build();
+ this.snsMessageHandler = new SnsMessageHandler(sns);
+
+ }
+
+ private void destroy() {
+ this.snsMessageHandler = null;
+ }
+
+ @Override
+ protected RuleMsgHandler getRuleMsgHandler() {
+ return snsMessageHandler;
+ }
+
+ @Override
+ public void resume(PluginContext ctx) {
+ init();
+ }
+
+ @Override
+ public void suspend(PluginContext ctx) {
+ destroy();
+ }
+
+ @Override
+ public void stop(PluginContext ctx) {
+ destroy();
+ }
+}
diff --git a/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java
new file mode 100644
index 0000000..dee78f3
--- /dev/null
+++ b/extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sns.plugin;
+
+import lombok.Data;
+
+/**
+ * Created by Valerii Sosliuk on 11/5/2017.
+ */
+@Data
+public class SnsPluginConfiguration {
+
+ private String accessKeyId;
+ private String secretAccessKey;
+ private String region;
+
+}
diff --git a/extensions/extension-sns/src/main/resources/SnsPluginDescriptor.json b/extensions/extension-sns/src/main/resources/SnsPluginDescriptor.json
new file mode 100644
index 0000000..04e23e7
--- /dev/null
+++ b/extensions/extension-sns/src/main/resources/SnsPluginDescriptor.json
@@ -0,0 +1,30 @@
+{
+ "schema": {
+ "title": "SNS Plugin Configuration",
+ "type": "object",
+ "properties": {
+ "accessKeyId": {
+ "title": "Access Key ID",
+ "type": "string"
+ },
+ "secretAccessKey": {
+ "title": "Secret Access Key",
+ "type": "string"
+ },
+ "region": {
+ "title": "Region",
+ "type": "string"
+ }
+ },
+ "required": [
+ "accessKeyId",
+ "secretAccessKey",
+ "region"
+ ]
+ },
+ "form": [
+ "accessKeyId",
+ "secretAccessKey",
+ "region"
+ ]
+}
\ No newline at end of file
diff --git a/extensions/extension-sns/src/main/resources/SnsTopicActionDescriptor.json b/extensions/extension-sns/src/main/resources/SnsTopicActionDescriptor.json
new file mode 100644
index 0000000..a8a2793
--- /dev/null
+++ b/extensions/extension-sns/src/main/resources/SnsTopicActionDescriptor.json
@@ -0,0 +1,34 @@
+{
+ "schema": {
+ "title": "SNS Topic Action Configuration",
+ "type": "object",
+ "properties": {
+ "sync": {
+ "title": "Requires delivery confirmation",
+ "type": "boolean"
+ },
+ "topicArn": {
+ "title": "Topic ARN",
+ "type": "string"
+ },
+ "template": {
+ "title": "Body Template",
+ "type": "string"
+ }
+ },
+ "required": [
+ "sync",
+ "topicArn",
+ "template"
+ ]
+ },
+ "form": [
+ "sync",
+ "topicArn",
+ {
+ "key": "template",
+ "type": "textarea",
+ "rows": 5
+ }
+ ]
+}
\ No newline at end of file
extensions/extention-sqs/pom.xml 81(+81 -0)
diff --git a/extensions/extention-sqs/pom.xml b/extensions/extention-sqs/pom.xml
new file mode 100644
index 0000000..1e16912
--- /dev/null
+++ b/extensions/extention-sqs/pom.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright © 2016-2017 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>extensions</artifactId>
+ <groupId>org.thingsboard</groupId>
+ <version>1.4.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.thingsboard.extensions</groupId>
+ <artifactId>extension-sqs</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Thingsboard Server SQS Extension</name>
+ <url>http://thingsboard.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <main.dir>${basedir}/../..</main.dir>
+ <aws.sdk.version>1.11.229</aws.sdk.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.thingsboard</groupId>
+ <artifactId>extensions-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard</groupId>
+ <artifactId>extensions-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.amazonaws</groupId>
+ <artifactId>aws-java-sdk-sqs</artifactId>
+ <version>${aws.sdk.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptors>
+ <descriptor>src/assembly/extension.xml</descriptor>
+ </descriptors>
+ </configuration>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/extensions/extention-sqs/src/assembly/extension.xml b/extensions/extention-sqs/src/assembly/extension.xml
new file mode 100644
index 0000000..2395d55
--- /dev/null
+++ b/extensions/extention-sqs/src/assembly/extension.xml
@@ -0,0 +1,37 @@
+<!--
+
+ Copyright © 2016-2017 The Thingsboard Authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
+ <id>extension</id>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <dependencySets>
+ <dependencySet>
+ <outputDirectory>/</outputDirectory>
+ <useProjectArtifact>true</useProjectArtifact>
+ <unpack>true</unpack>
+ <scope>runtime</scope>
+ <excludes>
+
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionMsg.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionMsg.java
new file mode 100644
index 0000000..c465063
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionMsg.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.fifo;
+
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg;
+
+/**
+ * Created by Valerii Sosliuk on 11/10/2017.
+ */
+public class SqsFifoQueueActionMsg extends AbstractRuleToPluginMsg<SqsFifoQueueActionPayload> {
+
+ public SqsFifoQueueActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, SqsFifoQueueActionPayload payload) {
+ super(tenantId, customerId, deviceId, payload);
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionPayload.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionPayload.java
new file mode 100644
index 0000000..692cd90
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionPayload.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.fifo;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.msg.session.MsgType;
+
+import java.io.Serializable;
+
+/**
+ * Created by Valerii Sosliuk on 11/10/2017.
+ */
+@Data
+@Builder
+public class SqsFifoQueueActionPayload implements Serializable {
+
+ private final String queue;
+ private final String msgBody;
+ private final String deviceId;
+
+ private final Integer requestId;
+ private final MsgType msgType;
+ private final boolean sync;
+
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginAction.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginAction.java
new file mode 100644
index 0000000..f107ea1
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginAction.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.fifo;
+
+import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
+import org.thingsboard.server.extensions.api.component.Action;
+import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
+import org.thingsboard.server.extensions.api.rules.RuleContext;
+import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueueActionMsg;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueueActionPayload;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueuePluginActionConfiguration;
+
+import java.util.Optional;
+
+/**
+ * Created by Valerii Sosliuk on 11/5/2017.
+ */
+@Action(name = "SQS Fifo Queue Action", descriptor = "SqsFifoQueueActionDescriptor.json", configuration = SqsFifoQueuePluginActionConfiguration.class)
+public class SqsFifoQueuePluginAction extends AbstractTemplatePluginAction<SqsFifoQueuePluginActionConfiguration> {
+
+ @Override
+ protected Optional<RuleToPluginMsg> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) {
+ SqsFifoQueueActionPayload.SqsFifoQueueActionPayloadBuilder builder = SqsFifoQueueActionPayload.builder();
+ builder.msgType(payload.getMsgType());
+ builder.requestId(payload.getRequestId());
+ builder.queue(configuration.getQueue());
+ builder.deviceId(msg.getDeviceId().toString());
+ builder.msgBody(getMsgBody(ctx, msg));
+ return Optional.of(new SqsFifoQueueActionMsg(msg.getTenantId(),
+ msg.getCustomerId(),
+ msg.getDeviceId(),
+ builder.build()));
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginActionConfiguration.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginActionConfiguration.java
new file mode 100644
index 0000000..d100325
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginActionConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.fifo;
+
+import lombok.Data;
+import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration;
+
+/**
+ * Created by Valerii Sosliuk on 11/10/2017.
+ */
+@Data
+public class SqsFifoQueuePluginActionConfiguration implements TemplateActionConfiguration {
+
+ private String queue;
+ private String template;
+ private boolean sync;
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionMsg.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionMsg.java
new file mode 100644
index 0000000..6666b27
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionMsg.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.standard;
+
+import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg;
+
+/**
+ * Created by Valerii Sosliuk on 11/6/2017.
+ */
+public class SqsStandardQueueActionMsg extends AbstractRuleToPluginMsg<SqsStandardQueueActionPayload> {
+
+ public SqsStandardQueueActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, SqsStandardQueueActionPayload payload) {
+ super(tenantId, customerId, deviceId, payload);
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionPayload.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionPayload.java
new file mode 100644
index 0000000..ce3dd27
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionPayload.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.standard;
+
+import lombok.Builder;
+import lombok.Data;
+import org.thingsboard.server.common.msg.session.MsgType;
+
+import java.io.Serializable;
+
+/**
+ * Created by Valerii Sosliuk on 11/6/2017.
+ */
+@Data
+@Builder
+public class SqsStandardQueueActionPayload implements Serializable {
+
+ private final String queue;
+ private final String msgBody;
+ private final int delaySeconds;
+
+ private final Integer requestId;
+ private final MsgType msgType;
+ private final boolean sync;
+
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginAction.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginAction.java
new file mode 100644
index 0000000..1dd9d56
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginAction.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.standard;
+
+import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
+import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg;
+import org.thingsboard.server.extensions.api.component.Action;
+import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
+import org.thingsboard.server.extensions.api.rules.RuleContext;
+import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction;
+
+import java.util.Optional;
+
+/**
+ * Created by Valerii Sosliuk on 11/5/2017.
+ */
+@Action(name = "SQS Standard Queue Action", descriptor = "SqsStandardQueueActionDescriptor.json", configuration = SqsStandardQueuePluginActionConfiguration.class)
+public class SqsStandardQueuePluginAction extends AbstractTemplatePluginAction<SqsStandardQueuePluginActionConfiguration> {
+
+ @Override
+ protected Optional<RuleToPluginMsg> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) {
+ SqsStandardQueueActionPayload.SqsStandardQueueActionPayloadBuilder builder = SqsStandardQueueActionPayload.builder();
+ builder.msgType(payload.getMsgType());
+ builder.requestId(payload.getRequestId());
+ builder.queue(configuration.getQueue());
+ builder.delaySeconds(configuration.getDelaySeconds());
+ builder.msgBody(getMsgBody(ctx, msg));
+ return Optional.of(new SqsStandardQueueActionMsg(msg.getTenantId(),
+ msg.getCustomerId(),
+ msg.getDeviceId(),
+ builder.build()));
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginActionConfiguration.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginActionConfiguration.java
new file mode 100644
index 0000000..5c21ef5
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginActionConfiguration.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.action.standard;
+
+import lombok.Data;
+import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration;
+
+/**
+ * Created by Valerii Sosliuk on 11/6/2017.
+ */
+@Data
+public class SqsStandardQueuePluginActionConfiguration implements TemplateActionConfiguration {
+
+ private String queue;
+ private int delaySeconds;
+ private boolean sync;
+ private String template;
+
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsMessageHandler.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsMessageHandler.java
new file mode 100644
index 0000000..b71e0b1
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsMessageHandler.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.plugin;
+
+import com.amazonaws.services.sqs.AmazonSQS;
+import com.amazonaws.services.sqs.model.SendMessageRequest;
+import com.amazonaws.services.sqs.model.SendMessageResult;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse;
+import org.thingsboard.server.extensions.api.plugins.PluginContext;
+import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
+import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg;
+import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;
+import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
+import org.thingsboard.server.extensions.api.rules.RuleException;
+import org.thingsboard.server.extensions.sqs.action.fifo.SqsFifoQueueActionMsg;
+import org.thingsboard.server.extensions.sqs.action.fifo.SqsFifoQueueActionPayload;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueueActionMsg;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueueActionPayload;
+
+/**
+ * Created by Valerii Sosliuk on 11/15/2017.
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class SqsMessageHandler implements RuleMsgHandler {
+
+ private final AmazonSQS sqs;
+
+ @Override
+ public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException {
+ if (msg instanceof SqsStandardQueueActionMsg) {
+ sendMessageToStandardQueue(ctx, tenantId, ruleId, msg);
+ return;
+ }
+ if (msg instanceof SqsFifoQueueActionMsg) {
+ sendMessageToFifoQueue(ctx, tenantId, ruleId, msg);
+ return;
+ }
+ throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!");
+ }
+
+ private void sendMessageToStandardQueue(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) {
+ SqsStandardQueueActionPayload payload = ((SqsStandardQueueActionMsg) msg).getPayload();
+ SendMessageRequest sendMsgRequest = new SendMessageRequest()
+ .withDelaySeconds(payload.getDelaySeconds())
+ .withQueueUrl(payload.getQueue())
+ .withMessageBody(payload.getMsgBody());
+ sqs.sendMessage(sendMsgRequest);
+ if (payload.isSync()) {
+ ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId,
+ BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId())));
+ }
+ }
+
+ private void sendMessageToFifoQueue(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) {
+ SqsFifoQueueActionPayload payload = ((SqsFifoQueueActionMsg) msg).getPayload();
+ SendMessageRequest sendMsgRequest = new SendMessageRequest()
+ .withQueueUrl(payload.getQueue())
+ .withMessageBody(payload.getMsgBody())
+ .withMessageGroupId(payload.getDeviceId());
+ sqs.sendMessage(sendMsgRequest);
+ if (payload.isSync()) {
+ ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId,
+ BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId())));
+ }
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPlugin.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPlugin.java
new file mode 100644
index 0000000..3f0252e
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPlugin.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.plugin;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.sqs.AmazonSQS;
+import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
+import org.thingsboard.server.extensions.api.component.Plugin;
+import org.thingsboard.server.extensions.api.plugins.AbstractPlugin;
+import org.thingsboard.server.extensions.api.plugins.PluginContext;
+import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
+import org.thingsboard.server.extensions.sqs.action.fifo.SqsFifoQueuePluginAction;
+import org.thingsboard.server.extensions.sqs.action.standard.SqsStandardQueuePluginAction;
+
+/**
+ * Created by Valerii Sosliuk on 11/6/2017.
+ */
+@Plugin(name = "SQS Plugin", actions = {SqsStandardQueuePluginAction.class, SqsFifoQueuePluginAction.class},
+ descriptor = "SqsPluginDescriptor.json", configuration = SqsPluginConfiguration.class)
+public class SqsPlugin extends AbstractPlugin<SqsPluginConfiguration> {
+
+ private SqsMessageHandler sqsMessageHandler;
+ private SqsPluginConfiguration configuration;
+
+ @Override
+ public void init(SqsPluginConfiguration configuration) {
+ this.configuration = configuration;
+ init();
+ }
+
+ private void init() {
+ AWSCredentials awsCredentials = new BasicAWSCredentials(configuration.getAccessKeyId(), configuration.getSecretAccessKey());
+ AmazonSQS sqs = AmazonSQSClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
+ .withRegion(Regions.fromName(configuration.getRegion())).build();
+ this.sqsMessageHandler = new SqsMessageHandler(sqs);
+
+ }
+
+ private void destroy() {
+ this.sqsMessageHandler = null;
+ }
+
+ @Override
+ protected RuleMsgHandler getRuleMsgHandler() {
+ return sqsMessageHandler;
+ }
+
+ @Override
+ public void resume(PluginContext ctx) {
+ init();
+ }
+
+ @Override
+ public void suspend(PluginContext ctx) {
+ destroy();
+ }
+
+ @Override
+ public void stop(PluginContext ctx) {
+ destroy();
+ }
+}
diff --git a/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPluginConfiguration.java b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPluginConfiguration.java
new file mode 100644
index 0000000..a93b7f3
--- /dev/null
+++ b/extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPluginConfiguration.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs.plugin;
+
+import lombok.Data;
+
+/**
+ * Created by Valerii Sosliuk on 11/5/2017.
+ */
+@Data
+public class SqsPluginConfiguration {
+
+ private String accessKeyId;
+ private String secretAccessKey;
+ private String region;
+
+}
diff --git a/extensions/extention-sqs/src/main/resources/SqsFifoQueueActionDescriptor.json b/extensions/extention-sqs/src/main/resources/SqsFifoQueueActionDescriptor.json
new file mode 100644
index 0000000..05bfaec
--- /dev/null
+++ b/extensions/extention-sqs/src/main/resources/SqsFifoQueueActionDescriptor.json
@@ -0,0 +1,34 @@
+{
+ "schema": {
+ "title": "SQS FIFO Queue Action Configuration",
+ "type": "object",
+ "properties": {
+ "sync": {
+ "title": "Requires delivery confirmation",
+ "type": "boolean"
+ },
+ "queue": {
+ "title": "Queue URL",
+ "type": "string"
+ },
+ "template": {
+ "title": "Body Template",
+ "type": "string"
+ }
+ },
+ "required": [
+ "sync",
+ "queue",
+ "template"
+ ]
+ },
+ "form": [
+ "sync",
+ "queue",
+ {
+ "key": "template",
+ "type": "textarea",
+ "rows": 5
+ }
+ ]
+}
\ No newline at end of file
diff --git a/extensions/extention-sqs/src/main/resources/SqsPluginDescriptor.json b/extensions/extention-sqs/src/main/resources/SqsPluginDescriptor.json
new file mode 100644
index 0000000..407468b
--- /dev/null
+++ b/extensions/extention-sqs/src/main/resources/SqsPluginDescriptor.json
@@ -0,0 +1,30 @@
+{
+ "schema": {
+ "title": "SQS Plugin Configuration",
+ "type": "object",
+ "properties": {
+ "accessKeyId": {
+ "title": "Access Key ID",
+ "type": "string"
+ },
+ "secretAccessKey": {
+ "title": "Secret Access Key",
+ "type": "string"
+ },
+ "region": {
+ "title": "Region",
+ "type": "string"
+ }
+ },
+ "required": [
+ "accessKeyId",
+ "secretAccessKey",
+ "region"
+ ]
+ },
+ "form": [
+ "accessKeyId",
+ "secretAccessKey",
+ "region"
+ ]
+}
\ No newline at end of file
diff --git a/extensions/extention-sqs/src/main/resources/SqsStandardQueueActionDescriptor.json b/extensions/extention-sqs/src/main/resources/SqsStandardQueueActionDescriptor.json
new file mode 100644
index 0000000..f5502e6
--- /dev/null
+++ b/extensions/extention-sqs/src/main/resources/SqsStandardQueueActionDescriptor.json
@@ -0,0 +1,41 @@
+{
+ "schema": {
+ "title": "SQS Standard Queue Action Configuration",
+ "type": "object",
+ "properties": {
+ "sync": {
+ "title": "Requires delivery confirmation",
+ "type": "boolean"
+ },
+ "queue": {
+ "title": "Queue URL",
+ "type": "string"
+ },
+ "delaySeconds": {
+ "title": "Delay Seconds",
+ "type": "integer",
+ "default": 0
+ },
+ "template": {
+ "title": "Body Template",
+ "type": "string"
+ }
+ },
+ "required": [
+ "sync",
+ "queue",
+ "delaySeconds",
+ "template"
+ ]
+ },
+ "form": [
+ "sync",
+ "queue",
+ "delaySeconds",
+ {
+ "key": "template",
+ "type": "textarea",
+ "rows": 5
+ }
+ ]
+}
\ No newline at end of file
diff --git a/extensions/extention-sqs/src/test/java/org/thingsboard/server/extensions/sqs/SqsDemoClient.java b/extensions/extention-sqs/src/test/java/org/thingsboard/server/extensions/sqs/SqsDemoClient.java
new file mode 100644
index 0000000..17c9b08
--- /dev/null
+++ b/extensions/extention-sqs/src/test/java/org/thingsboard/server/extensions/sqs/SqsDemoClient.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.extensions.sqs;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.sqs.AmazonSQS;
+import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
+import com.amazonaws.services.sqs.model.DeleteMessageRequest;
+import com.amazonaws.services.sqs.model.Message;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * Created by Valerii Sosliuk on 11/10/2017.
+ */
+@Slf4j
+public class SqsDemoClient {
+
+ private static final String ACCESS_KEY_ID = "$ACCES_KEY_ID";
+ private static final String SECRET_ACCESS_KEY = "$SECRET_ACCESS_KEY";
+
+ private static final String QUEUE_URL = "$QUEUE_URL";
+ private static final String REGION = "us-east-1";
+
+ public static void main(String[] args) {
+ log.info("Starting SQS Demo Clinent...");
+ AWSCredentials awsCredentials = new BasicAWSCredentials(ACCESS_KEY_ID, SECRET_ACCESS_KEY);
+ AmazonSQS sqs = AmazonSQSClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
+ .withRegion(Regions.fromName(REGION)).build();
+ SqsDemoClient client = new SqsDemoClient();
+ client.pollMessages(sqs);
+ }
+
+ private void pollMessages(AmazonSQS sqs) {
+ log.info("Polling messages");
+ while (true) {
+ List<Message> messages = sqs.receiveMessage(QUEUE_URL).getMessages();
+ messages.forEach(m -> {
+ log.info("Message Received: " + m.getBody());
+ System.out.println(m.getBody());
+ DeleteMessageRequest deleteMessageRequest = new DeleteMessageRequest(QUEUE_URL, m.getReceiptHandle());
+ sqs.deleteMessage(deleteMessageRequest);
+ });
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/extensions/extention-sqs/src/test/resources/logback.xml b/extensions/extention-sqs/src/test/resources/logback.xml
new file mode 100644
index 0000000..757aa32
--- /dev/null
+++ b/extensions/extention-sqs/src/test/resources/logback.xml
@@ -0,0 +1,10 @@
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="INFO">
+ <appender-ref ref="STDOUT"/>
+ </root>
+</configuration>
\ No newline at end of file
extensions/pom.xml 2(+2 -0)
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 30c9395..e1c2084 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -39,6 +39,8 @@
<module>extension-rest-api-call</module>
<module>extension-kafka</module>
<module>extension-mqtt</module>
+ <module>extention-sqs</module>
+ <module>extension-sns</module>
</modules>
</project>
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java
index 0b8e992..7e92fc1 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java
@@ -128,12 +128,16 @@ public class TelemetryRestMsgHandler extends DefaultRestMsgHandler {
Optional<Long> interval = request.getLongParamValue("interval");
Optional<Integer> limit = request.getIntParamValue("limit");
+ // If some of these params are specified, they all must be
if (startTs.isPresent() || endTs.isPresent() || interval.isPresent() || limit.isPresent()) {
- if (!startTs.isPresent() || !endTs.isPresent() || !interval.isPresent()) {
+ if (!startTs.isPresent() || !endTs.isPresent() || !interval.isPresent() || interval.get() < 0) {
msg.getResponseHolder().setResult(new ResponseEntity<>(HttpStatus.BAD_REQUEST));
return;
}
- Aggregation agg = Aggregation.valueOf(request.getParameter("agg", Aggregation.NONE.name()));
+
+ // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
+ Aggregation agg = (interval.isPresent() && interval.get() == 0) ? Aggregation.valueOf(Aggregation.NONE.name()) :
+ Aggregation.valueOf(request.getParameter("agg", Aggregation.NONE.name()));
List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs.get(), endTs.get(), interval.get(), limit.orElse(TelemetryWebsocketMsgHandler.DEFAULT_LIMIT), agg))
.collect(Collectors.toList());
pom.xml 12(+12 -0)
diff --git a/pom.xml b/pom.xml
index 1f9a92d..e45fc3e 100755
--- a/pom.xml
+++ b/pom.xml
@@ -351,6 +351,18 @@
<version>${project.version}</version>
</dependency>
<dependency>
+ <groupId>org.thingsboard.extensions</groupId>
+ <artifactId>extension-sqs</artifactId>
+ <classifier>extension</classifier>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.thingsboard.extensions</groupId>
+ <artifactId>extension-sns</artifactId>
+ <classifier>extension</classifier>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>data</artifactId>
<version>${project.version}</version>
resume.bat 18(+18 -0)
diff --git a/resume.bat b/resume.bat
new file mode 100644
index 0000000..c3c0e1d
--- /dev/null
+++ b/resume.bat
@@ -0,0 +1,18 @@
+@REM
+@REM Copyright © 2016-2017 The Thingsboard Authors
+@REM
+@REM Licensed under the Apache License, Version 2.0 (the "License");
+@REM you may not use this file except in compliance with the License.
+@REM You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+@REM
+
+mvn clean install -rf :application
+
tools/src/main/python/mqtt-send-telemetry.py 36(+36 -0)
diff --git a/tools/src/main/python/mqtt-send-telemetry.py b/tools/src/main/python/mqtt-send-telemetry.py
new file mode 100644
index 0000000..8c4263d
--- /dev/null
+++ b/tools/src/main/python/mqtt-send-telemetry.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2016-2017 The Thingsboard Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import paho.mqtt.client as mqtt
+from time import sleep
+import random
+
+broker="test.mosquitto.org"
+topic_pub='v1/devices/me/telemetry'
+
+
+client = mqtt.Client()
+
+client.username_pw_set("TEST_TOKEN")
+client.connect('127.0.0.1', 1883, 1)
+
+for i in range(5):
+ x = random.randrange(20, 100)
+ print x
+ msg = '{"windSpeed":"'+ str(x) + '"}'
+ client.publish(topic_pub, msg)
+ sleep(0.1)
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
index 7b527df..7bed03a 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.transport.mqtt.session;
import com.google.gson.Gson;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.netty.buffer.ByteBuf;
@@ -24,6 +25,7 @@ import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.codec.mqtt.*;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.SessionId;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.msg.core.*;
import org.thingsboard.server.common.msg.kv.AttributesKVMsg;
@@ -35,6 +37,7 @@ import org.thingsboard.server.transport.mqtt.MqttTopics;
import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import java.nio.charset.Charset;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@@ -83,7 +86,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
if (responseMsg.isSuccess()) {
MsgType requestMsgType = responseMsg.getRequestMsgType();
Integer requestId = responseMsg.getRequestId();
- if (requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) {
+ if (requestId >= 0 && requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) {
return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId));
}
}
@@ -135,40 +138,43 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext {
if (responseData.isPresent()) {
AttributesKVMsg msg = responseData.get();
if (msg.getClientAttributes() != null) {
- msg.getClientAttributes().forEach(v -> addValueToJson(result, "value", v));
+ addValues(result, msg.getClientAttributes());
}
if (msg.getSharedAttributes() != null) {
- msg.getSharedAttributes().forEach(v -> addValueToJson(result, "value", v));
+ addValues(result, msg.getSharedAttributes());
}
}
return createMqttPublishMsg(topic, result);
}
+ private void addValues(JsonObject result, List<AttributeKvEntry> kvList) {
+ if (kvList.size() == 1) {
+ addValueToJson(result, "value", kvList.get(0));
+ } else {
+ JsonObject values;
+ if (result.has("values")) {
+ values = result.get("values").getAsJsonObject();
+ } else {
+ values = new JsonObject();
+ result.add("values", values);
+ }
+ kvList.forEach(value -> addValueToJson(values, value.getKey(), value));
+ }
+ }
+
private void addValueToJson(JsonObject json, String name, KvEntry entry) {
switch (entry.getDataType()) {
case BOOLEAN:
- Optional<Boolean> booleanValue = entry.getBooleanValue();
- if (booleanValue.isPresent()) {
- json.addProperty(name, booleanValue.get());
- }
+ entry.getBooleanValue().ifPresent(aBoolean -> json.addProperty(name, aBoolean));
break;
case STRING:
- Optional<String> stringValue = entry.getStrValue();
- if (stringValue.isPresent()) {
- json.addProperty(name, stringValue.get());
- }
+ entry.getStrValue().ifPresent(aString -> json.addProperty(name, aString));
break;
case DOUBLE:
- Optional<Double> doubleValue = entry.getDoubleValue();
- if (doubleValue.isPresent()) {
- json.addProperty(name, doubleValue.get());
- }
+ entry.getDoubleValue().ifPresent(aDouble -> json.addProperty(name, aDouble));
break;
case LONG:
- Optional<Long> longValue = entry.getLongValue();
- if (longValue.isPresent()) {
- json.addProperty(name, longValue.get());
- }
+ entry.getLongValue().ifPresent(aLong -> json.addProperty(name, aLong));
break;
}
}
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
index 00d1f0c..5bccf49 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
@@ -41,10 +41,7 @@ import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.transport.mqtt.MqttTransportHandler;
import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
import java.util.stream.Collectors;
import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload;
@@ -193,13 +190,22 @@ public class GatewaySessionCtx {
int requestId = jsonObj.get("id").getAsInt();
String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString();
boolean clientScope = jsonObj.get("client").getAsBoolean();
- String key = jsonObj.get("key").getAsString();
+ Set<String> keys;
+ if (jsonObj.has("key")) {
+ keys = Collections.singleton(jsonObj.get("key").getAsString());
+ } else {
+ JsonArray keysArray = jsonObj.get("keys").getAsJsonArray();
+ keys = new HashSet<>();
+ for (JsonElement keyObj : keysArray) {
+ keys.add(keyObj.getAsString());
+ }
+ }
BasicGetAttributesRequest request;
if (clientScope) {
- request = new BasicGetAttributesRequest(requestId, Collections.singleton(key), null);
+ request = new BasicGetAttributesRequest(requestId, keys, null);
} else {
- request = new BasicGetAttributesRequest(requestId, null, Collections.singleton(key));
+ request = new BasicGetAttributesRequest(requestId, null, keys);
}
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName);
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(),
@@ -251,7 +257,7 @@ public class GatewaySessionCtx {
}
private void ack(MqttPublishMessage msg) {
- if(msg.variableHeader().messageId() > 0) {
+ if (msg.variableHeader().messageId() > 0) {
writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId()));
}
}
ui/src/app/api/login.service.js 16(+8 -8)
diff --git a/ui/src/app/api/login.service.js b/ui/src/app/api/login.service.js
index 272e4df..74f9587 100644
--- a/ui/src/app/api/login.service.js
+++ b/ui/src/app/api/login.service.js
@@ -65,8 +65,8 @@ function LoginService($http, $q) {
function sendResetPasswordLink(email) {
var deferred = $q.defer();
- var url = '/api/noauth/resetPasswordByEmail?email=' + email;
- $http.post(url, null).then(function success(response) {
+ var url = '/api/noauth/resetPasswordByEmail';
+ $http.post(url, {email: email}).then(function success(response) {
deferred.resolve(response);
}, function fail() {
deferred.reject();
@@ -76,8 +76,8 @@ function LoginService($http, $q) {
function resetPassword(resetToken, password) {
var deferred = $q.defer();
- var url = '/api/noauth/resetPassword?resetToken=' + resetToken + '&password=' + password;
- $http.post(url, null).then(function success(response) {
+ var url = '/api/noauth/resetPassword';
+ $http.post(url, {resetToken: resetToken, password: password}).then(function success(response) {
deferred.resolve(response);
}, function fail() {
deferred.reject();
@@ -87,8 +87,8 @@ function LoginService($http, $q) {
function activate(activateToken, password) {
var deferred = $q.defer();
- var url = '/api/noauth/activate?activateToken=' + activateToken + '&password=' + password;
- $http.post(url, null).then(function success(response) {
+ var url = '/api/noauth/activate';
+ $http.post(url, {activateToken: activateToken, password: password}).then(function success(response) {
deferred.resolve(response);
}, function fail() {
deferred.reject();
@@ -98,8 +98,8 @@ function LoginService($http, $q) {
function changePassword(currentPassword, newPassword) {
var deferred = $q.defer();
- var url = '/api/auth/changePassword?currentPassword=' + currentPassword + '&newPassword=' + newPassword;
- $http.post(url, null).then(function success(response) {
+ var url = '/api/auth/changePassword';
+ $http.post(url, {currentPassword: currentPassword, newPassword: newPassword}).then(function success(response) {
deferred.resolve(response);
}, function fail() {
deferred.reject();
ui/src/app/api/subscription.js 3(+2 -1)
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index 4786bf9..022a31c 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -654,8 +654,9 @@ export default class Subscription {
if (!sourceData.data.length) {
update = false;
} else if (prevData && prevData[0] && prevData[0].length > 1 && sourceData.data.length > 0) {
+ var prevTs = prevData[0][0];
var prevValue = prevData[0][1];
- if (prevValue === sourceData.data[0][1]) {
+ if (prevTs === sourceData.data[0][0] && prevValue === sourceData.data[0][1]) {
update = false;
}
}
ui/src/app/api/user.service.js 19(+10 -9)
diff --git a/ui/src/app/api/user.service.js b/ui/src/app/api/user.service.js
index 1668657..d09387b 100644
--- a/ui/src/app/api/user.service.js
+++ b/ui/src/app/api/user.service.js
@@ -302,7 +302,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
$rootScope.forceFullscreen = true;
fetchAllowedDashboardIds();
} else if (currentUser.userId) {
- getUser(currentUser.userId).then(
+ getUser(currentUser.userId, true).then(
function success(user) {
currentUserDetails = user;
updateUserLang();
@@ -319,6 +319,7 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
},
function fail() {
deferred.reject();
+ logout();
}
)
} else {
@@ -414,19 +415,19 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
}
$http.post(url, user).then(function success(response) {
deferred.resolve(response.data);
- }, function fail(response) {
- deferred.reject(response.data);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
- function getUser(userId) {
+ function getUser(userId, ignoreErrors) {
var deferred = $q.defer();
var url = '/api/user/' + userId;
- $http.get(url).then(function success(response) {
+ $http.get(url, { ignoreErrors: ignoreErrors }).then(function success(response) {
deferred.resolve(response.data);
- }, function fail(response) {
- deferred.reject(response.data);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
@@ -436,8 +437,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
var url = '/api/user/' + userId;
$http.delete(url).then(function success() {
deferred.resolve();
- }, function fail(response) {
- deferred.reject(response.data);
+ }, function fail() {
+ deferred.reject();
});
return deferred.promise;
}
diff --git a/ui/src/app/components/details-sidenav.scss b/ui/src/app/components/details-sidenav.scss
index 54a029d..159162a 100644
--- a/ui/src/app/components/details-sidenav.scss
+++ b/ui/src/app/components/details-sidenav.scss
@@ -16,7 +16,10 @@
@import '../../scss/constants';
.tb-details-title {
- font-size: 1.600rem;
+ font-size: 1.000rem;
+ @media (min-width: $layout-breakpoint-gt-sm) {
+ font-size: 1.600rem;
+ }
font-weight: 400;
text-transform: uppercase;
margin: 20px 8px 0 0;
ui/src/app/components/js-func.scss 2(+1 -1)
diff --git a/ui/src/app/components/js-func.scss b/ui/src/app/components/js-func.scss
index 7c36b81..3124dc1 100644
--- a/ui/src/app/components/js-func.scss
+++ b/ui/src/app/components/js-func.scss
@@ -22,7 +22,7 @@ tb-js-func {
border: 1px solid #C0C0C0;
height: 100%;
#tb-javascript-input {
- min-width: 400px;
+ min-width: 200px;
min-height: 200px;
width: 100%;
height: 100%;
diff --git a/ui/src/app/components/js-func.tpl.html b/ui/src/app/components/js-func.tpl.html
index ee3c2e0..bec7991 100644
--- a/ui/src/app/components/js-func.tpl.html
+++ b/ui/src/app/components/js-func.tpl.html
@@ -19,7 +19,7 @@
<div layout="row" layout-align="start center" style="height: 40px;">
<span style="font-style: italic;">function({{ functionArgsString }}) {</span>
<span flex></span>
- <md-button id="expand-button" aria-label="Fullscreen" class="md-icon-button tb-md-32 tb-fullscreen-button-style"></md-button>
+ <div id="expand-button" layout="column" aria-label="Fullscreen" class="md-button md-icon-button tb-md-32 tb-fullscreen-button-style"></div>
</div>
<div flex id="tb-javascript-panel" class="tb-js-func-panel" layout="column">
<div flex id="tb-javascript-input"
diff --git a/ui/src/app/entity/relation/relation-dialog.controller.js b/ui/src/app/entity/relation/relation-dialog.controller.js
index ae50e30..d13b4db 100644
--- a/ui/src/app/entity/relation/relation-dialog.controller.js
+++ b/ui/src/app/entity/relation/relation-dialog.controller.js
@@ -79,10 +79,8 @@ export default function RelationDialogController($scope, $mdDialog, types, entit
});
function updateEditorSize(element) {
- var newWidth = 600;
var newHeight = 200;
- angular.element('#tb-relation-additional-info', element).height(newHeight.toString() + "px")
- .width(newWidth.toString() + "px");
+ angular.element('#tb-relation-additional-info', element).height(newHeight.toString() + "px");
vm.editor.resize();
}
diff --git a/ui/src/app/entity/relation/relation-dialog.scss b/ui/src/app/entity/relation/relation-dialog.scss
index 55064df..1bd038b 100644
--- a/ui/src/app/entity/relation/relation-dialog.scss
+++ b/ui/src/app/entity/relation/relation-dialog.scss
@@ -19,7 +19,7 @@
border: 1px solid #C0C0C0;
height: 100%;
#tb-relation-additional-info {
- min-width: 600px;
+ min-width: 200px;
min-height: 200px;
width: 100%;
height: 100%;
diff --git a/ui/src/app/entity/relation/relation-dialog.tpl.html b/ui/src/app/entity/relation/relation-dialog.tpl.html
index 15b0c7f..8799287 100644
--- a/ui/src/app/entity/relation/relation-dialog.tpl.html
+++ b/ui/src/app/entity/relation/relation-dialog.tpl.html
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<md-dialog aria-label="{{ (vm.isAdd ? 'relation.add' : 'relation.edit' ) | translate }}" style="min-width: 400px;">
+<md-dialog aria-label="{{ (vm.isAdd ? 'relation.add' : 'relation.edit' ) | translate }}" style="min-width: 600px;">
<form name="theForm" ng-submit="vm.save()">
<md-toolbar>
<div class="md-toolbar-tools">
diff --git a/ui/src/app/home/home-links.controller.js b/ui/src/app/home/home-links.controller.js
index 273433b..799a0bc 100644
--- a/ui/src/app/home/home-links.controller.js
+++ b/ui/src/app/home/home-links.controller.js
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+import './home-links.scss';
+
/*@ngInject*/
export default function HomeLinksController($scope, menu) {
var vm = this;
ui/src/app/home/home-links.scss 26(+26 -0)
diff --git a/ui/src/app/home/home-links.scss b/ui/src/app/home/home-links.scss
new file mode 100644
index 0000000..e210334
--- /dev/null
+++ b/ui/src/app/home/home-links.scss
@@ -0,0 +1,26 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@import "../../scss/constants";
+
+.tb-home-links {
+ .md-headline {
+ font-size: 20px;
+ @media (min-width: $layout-breakpoint-xmd) {
+ font-size: 24px;
+ }
+ }
+}
\ No newline at end of file
ui/src/app/home/home-links.tpl.html 2(+1 -1)
diff --git a/ui/src/app/home/home-links.tpl.html b/ui/src/app/home/home-links.tpl.html
index 5eac69f..e27f20a 100644
--- a/ui/src/app/home/home-links.tpl.html
+++ b/ui/src/app/home/home-links.tpl.html
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<md-grid-list md-cols="2" md-cols-gt-sm="4" md-row-height="280px">
+<md-grid-list class="tb-home-links" md-cols="2" md-cols-lg="3" md-cols-gt-lg="4" md-row-height="280px">
<md-grid-tile md-colspan="2" md-colspan-gt-sm="{{section.places.length}}" ng-repeat="section in vm.model">
<md-card style='width: 100%;'>
<md-card-title>
ui/src/scss/main.scss 6(+5 -1)
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 076dbef..93418b2 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -494,11 +494,15 @@ md-tabs.tb-headless {
height: 100%;
max-width: 240px;
span {
- padding: 10px 10px 20px 10px;
+ padding: 0 0 20px 0;
font-size: 18px;
font-weight: 400;
white-space: normal;
line-height: 18px;
+ max-height: 18px;
+ min-height: 18px;
+ height: 18px;
+ margin: auto;
}
}