Browse Source

Merge branch 'master' into feature/api-limits

pull/440/head
Igor Kulikov 9 years ago
parent
commit
66dcb97409
  1. 6
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  2. 2
      application/src/main/java/org/thingsboard/server/actors/rule/RuleActorMessageProcessor.java
  3. 6
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  4. 21
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  5. 4
      application/src/main/resources/thingsboard.yml
  6. 5
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  7. 21
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  8. 1
      dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java
  9. 20
      dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java
  10. 8
      extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRestMsgHandler.java
  11. 81
      extensions/extension-sns/pom.xml
  12. 37
      extensions/extension-sns/src/assembly/extension.xml
  13. 31
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionMsg.java
  14. 37
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicActionPayload.java
  15. 45
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginAction.java
  16. 30
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/action/SnsTopicPluginActionConfiguration.java
  17. 63
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsMessageHandler.java
  18. 79
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPlugin.java
  19. 30
      extensions/extension-sns/src/main/java/org/thingsboard/server/extensions/sns/plugin/SnsPluginConfiguration.java
  20. 30
      extensions/extension-sns/src/main/resources/SnsPluginDescriptor.json
  21. 34
      extensions/extension-sns/src/main/resources/SnsTopicActionDescriptor.json
  22. 81
      extensions/extention-sqs/pom.xml
  23. 37
      extensions/extention-sqs/src/assembly/extension.xml
  24. 31
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionMsg.java
  25. 39
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueueActionPayload.java
  26. 49
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginAction.java
  27. 30
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/fifo/SqsFifoQueuePluginActionConfiguration.java
  28. 31
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionMsg.java
  29. 39
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueueActionPayload.java
  30. 46
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginAction.java
  31. 32
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/action/standard/SqsStandardQueuePluginActionConfiguration.java
  32. 84
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsMessageHandler.java
  33. 78
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPlugin.java
  34. 30
      extensions/extention-sqs/src/main/java/org/thingsboard/server/extensions/sqs/plugin/SqsPluginConfiguration.java
  35. 34
      extensions/extention-sqs/src/main/resources/SqsFifoQueueActionDescriptor.json
  36. 30
      extensions/extention-sqs/src/main/resources/SqsPluginDescriptor.json
  37. 41
      extensions/extention-sqs/src/main/resources/SqsStandardQueueActionDescriptor.json
  38. 69
      extensions/extention-sqs/src/test/java/org/thingsboard/server/extensions/sqs/SqsDemoClient.java
  39. 10
      extensions/extention-sqs/src/test/resources/logback.xml
  40. 2
      extensions/pom.xml
  41. 12
      pom.xml
  42. 18
      resume.bat
  43. 36
      tools/src/main/python/mqtt-send-telemetry.py
  44. 44
      transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java
  45. 22
      transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
  46. 16
      ui/src/app/api/login.service.js
  47. 3
      ui/src/app/api/subscription.js
  48. 19
      ui/src/app/api/user.service.js
  49. 5
      ui/src/app/components/details-sidenav.scss
  50. 2
      ui/src/app/components/js-func.scss
  51. 2
      ui/src/app/components/js-func.tpl.html
  52. 4
      ui/src/app/entity/relation/relation-dialog.controller.js
  53. 2
      ui/src/app/entity/relation/relation-dialog.scss
  54. 2
      ui/src/app/entity/relation/relation-dialog.tpl.html
  55. 3
      ui/src/app/home/home-links.controller.js
  56. 26
      ui/src/app/home/home-links.scss
  57. 2
      ui/src/app/home/home-links.tpl.html
  58. 6
      ui/src/scss/main.scss

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

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

6
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;
@ -147,6 +148,11 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
return new BCryptPasswordEncoder();
}
@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()

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

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

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

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

1
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

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

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

81
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>

37
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>

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

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

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

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

63
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() + "!");
}
}

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

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

30
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"
]
}

34
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
}
]
}

81
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>

37
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>

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

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

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

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

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

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

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

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

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

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

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

34
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
}
]
}

30
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"
]
}

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

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

10
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>

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

12
pom.xml

@ -350,6 +350,18 @@
<classifier>extension</classifier>
<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>

18
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

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

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

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

16
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();

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save