thingsboard-aplcache

Merge pull request #1330 from ShvaykaD/master getAttributes

12/21/2018 7:43:41 AM

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 e46a959..c0030cb 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
@@ -16,7 +16,6 @@
 package org.thingsboard.server.actors.device;
 
 import akka.actor.ActorContext;
-import akka.event.LoggingAdapter;
 import com.datastax.driver.core.utils.UUIDs;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
@@ -26,12 +25,12 @@ import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.protobuf.InvalidProtocolBufferException;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.thingsboard.rule.engine.api.RpcError;
 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
 import org.thingsboard.server.actors.ActorSystemContext;
 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
-import org.thingsboard.server.common.data.DataConstants;
 import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -42,7 +41,6 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
 import org.thingsboard.server.common.msg.TbMsg;
 import org.thingsboard.server.common.msg.TbMsgDataType;
 import org.thingsboard.server.common.msg.TbMsgMetaData;
-import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
 import org.thingsboard.server.common.msg.session.SessionMsgType;
 import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
@@ -81,12 +79,14 @@ import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
+import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE;
+import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
+
 /**
  * @author Andrew Shvayka
  */
@@ -263,10 +263,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
     }
 
     private void handleGetAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
-        ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.CLIENT_SCOPE, toOptionalSet(request.getClientAttributeNamesList()));
-        ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture = getAttributeKvEntries(deviceId, DataConstants.SHARED_SCOPE, toOptionalSet(request.getSharedAttributeNamesList()));
         int requestId = request.getRequestId();
-        Futures.addCallback(Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)), new FutureCallback<List<List<AttributeKvEntry>>>() {
+        Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<List<List<AttributeKvEntry>>>() {
             @Override
             public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
                 GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
@@ -287,16 +285,35 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
         });
     }
 
-    private ListenableFuture<List<AttributeKvEntry>> getAttributeKvEntries(DeviceId deviceId, String scope, Optional<Set<String>> names) {
-        if (names.isPresent()) {
-            if (!names.get().isEmpty()) {
-                return systemContext.getAttributesService().find(tenantId, deviceId, scope, names.get());
-            } else {
-                return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
-            }
+    private ListenableFuture<List<List<AttributeKvEntry>>> getAttributesKvEntries(GetAttributeRequestMsg request) {
+        ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;
+        ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;
+        if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+            clientAttributesFuture = findAllAttributesByScope(CLIENT_SCOPE);
+            sharedAttributesFuture = findAllAttributesByScope(SHARED_SCOPE);
+        } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+            clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE);
+            sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE);
+        } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
+            clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
+            sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE);
         } else {
-            return Futures.immediateFuture(Collections.emptyList());
+            sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
+            clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE);
         }
+        return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
+    }
+
+    private ListenableFuture<List<AttributeKvEntry>> findAllAttributesByScope(String scope) {
+        return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
+    }
+
+    private ListenableFuture<List<AttributeKvEntry>> findAttributesByScope(Set<String> attributesSet, String scope) {
+        return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet);
+    }
+
+    private Set<String> toSet(List<String> strings) {
+        return new HashSet<>(strings);
     }
 
     private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
@@ -368,7 +385,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
             AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
             if (msg.isDeleted()) {
                 List<String> sharedKeys = msg.getDeletedKeys().stream()
-                        .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
+                        .filter(key -> SHARED_SCOPE.equals(key.getScope()))
                         .map(AttributeKey::getAttributeKey)
                         .collect(Collectors.toList());
                 if (!sharedKeys.isEmpty()) {
@@ -376,7 +393,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
                     hasNotificationData = true;
                 }
             } else {
-                if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
+                if (SHARED_SCOPE.equals(msg.getScope())) {
                     List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
                     if (attributes.size() > 0) {
                         List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
@@ -545,14 +562,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
         return json;
     }
 
-    private Optional<Set<String>> toOptionalSet(List<String> strings) {
-        if (strings == null || strings.isEmpty()) {
-            return Optional.empty();
-        } else {
-            return Optional.of(new HashSet<>(strings));
-        }
-    }
-
     private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
         DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
                 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
index 4c3ac83..bbabc2e 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
@@ -32,7 +32,8 @@ import org.apache.http.impl.client.HttpClients;
 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
 import org.apache.http.ssl.SSLContextBuilder;
 import org.apache.http.ssl.SSLContexts;
-import org.junit.*;
+import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
@@ -43,7 +44,10 @@ import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
 
-import javax.net.ssl.*;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
 import java.net.URI;
 import java.security.cert.X509Certificate;
 import java.util.List;
@@ -54,6 +58,7 @@ import java.util.Random;
 public abstract class AbstractContainerTest {
     protected static final String HTTPS_URL = "https://localhost";
     protected static final String WSS_URL = "wss://localhost";
+    protected static String TB_TOKEN;
     protected static RestClient restClient;
     protected ObjectMapper mapper = new ObjectMapper();
 
diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
index a6e89de..bb3380f 100644
--- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
+++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
@@ -15,6 +15,7 @@
  */
 package org.thingsboard.server.msa.connectivity;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.collect.Sets;
 import org.junit.Assert;
 import org.junit.Test;
@@ -25,6 +26,17 @@ import org.thingsboard.server.msa.AbstractContainerTest;
 import org.thingsboard.server.msa.WsClient;
 import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
 
+
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.thingsboard.server.common.data.DataConstants.DEVICE;
+import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
+
 public class HttpClientTest extends AbstractContainerTest {
 
     @Test
@@ -52,6 +64,58 @@ public class HttpClientTest extends AbstractContainerTest {
         Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0)));
         Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73)));
 
-        restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
+        restClient.deleteDevice(device.getId());
+    }
+
+    @Test
+    public void getAttributes() throws Exception {
+        restClient.login("tenant@thingsboard.org", "tenant");
+        TB_TOKEN = restClient.getToken();
+
+        Device device = createDevice("test");
+        String accessToken = restClient.getCredentials(device.getId()).getCredentialsId();
+        assertNotNull(accessToken);
+
+        ResponseEntity deviceSharedAttributes = restClient.getRestTemplate()
+                .postForEntity(HTTPS_URL + "/api/plugins/telemetry/" + DEVICE + "/" + device.getId().toString() + "/attributes/" + SHARED_SCOPE, mapper.readTree(createPayload().toString()),
+                        ResponseEntity.class,
+                        accessToken);
+
+        Assert.assertTrue(deviceSharedAttributes.getStatusCode().is2xxSuccessful());
+
+        ResponseEntity deviceClientsAttributes = restClient.getRestTemplate()
+                .postForEntity(HTTPS_URL + "/api/v1/" + accessToken + "/attributes/", mapper.readTree(createPayload().toString()),
+                        ResponseEntity.class,
+                        accessToken);
+
+        Assert.assertTrue(deviceClientsAttributes.getStatusCode().is2xxSuccessful());
+
+        TimeUnit.SECONDS.sleep(3);
+
+        Optional<JsonNode> allOptional = restClient.getAttributes(accessToken, null, null);
+        assertTrue(allOptional.isPresent());
+
+
+        JsonNode all = allOptional.get();
+        assertEquals(2, all.size());
+        assertEquals(mapper.readTree(createPayload().toString()), all.get("shared"));
+        assertEquals(mapper.readTree(createPayload().toString()), all.get("client"));
+
+        Optional<JsonNode> sharedOptional = restClient.getAttributes(accessToken, null, "stringKey");
+        assertTrue(sharedOptional.isPresent());
+
+        JsonNode shared = sharedOptional.get();
+        assertEquals(shared.get("shared").get("stringKey"), mapper.readTree(createPayload().get("stringKey").toString()));
+        assertFalse(shared.has("client"));
+
+        Optional<JsonNode> clientOptional = restClient.getAttributes(accessToken, "longKey,stringKey", null);
+        assertTrue(clientOptional.isPresent());
+
+        JsonNode client = clientOptional.get();
+        assertFalse(client.has("shared"));
+        assertEquals(mapper.readTree(createPayload().get("longKey").toString()), client.get("client").get("longKey"));
+        assertEquals(client.get("client").get("stringKey"), mapper.readTree(createPayload().get("stringKey").toString()));
+
+        restClient.deleteDevice(device.getId());
     }
 }

tools/pom.xml 1(+0 -1)

diff --git a/tools/pom.xml b/tools/pom.xml
index c1c1ded..616d894 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -23,7 +23,6 @@
         <version>2.2.1-SNAPSHOT</version>
         <artifactId>thingsboard</artifactId>
     </parent>
-    <groupId>org.thingsboard</groupId>
     <artifactId>tools</artifactId>
     <packaging>jar</packaging>
 
diff --git a/tools/src/main/java/org/thingsboard/client/tools/RestClient.java b/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
index 14f8f9f..53ec22d 100644
--- a/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
+++ b/tools/src/main/java/org/thingsboard/client/tools/RestClient.java
@@ -108,6 +108,27 @@ public class RestClient implements ClientHttpRequestInterceptor {
         }
     }
 
+    public Optional<JsonNode> getAttributes(String accessToken, String clientKeys, String sharedKeys) {
+        Map<String, String> params = new HashMap<>();
+        params.put("accessToken", accessToken);
+        params.put("clientKeys", clientKeys);
+        params.put("sharedKeys", sharedKeys);
+        try {
+            ResponseEntity<JsonNode> telemetryEntity = restTemplate.getForEntity(baseURL + "/api/v1/{accessToken}/attributes?clientKeys={clientKeys}&sharedKeys={sharedKeys}", JsonNode.class, params);
+            return Optional.of(telemetryEntity.getBody());
+        } catch (HttpClientErrorException exception) {
+            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
+                return Optional.empty();
+            } else {
+                throw exception;
+            }
+        }
+    }
+
+    public Customer createCustomer(Customer customer) {
+        return restTemplate.postForEntity(baseURL + "/api/customer", customer, Customer.class).getBody();
+    }
+
     public Customer createCustomer(String title) {
         Customer customer = new Customer();
         customer.setTitle(title);
@@ -121,10 +142,6 @@ public class RestClient implements ClientHttpRequestInterceptor {
         return restTemplate.postForEntity(baseURL + "/api/device", device, Device.class).getBody();
     }
 
-    public void deleteDevice(DeviceId deviceId) {
-        restTemplate.delete(baseURL + "/api/device/" + deviceId.getId().toString());
-    }
-
     public DeviceCredentials updateDeviceCredentials(DeviceId deviceId, String token) {
         DeviceCredentials deviceCredentials = getCredentials(deviceId);
         deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
@@ -136,6 +153,14 @@ public class RestClient implements ClientHttpRequestInterceptor {
         return restTemplate.postForEntity(baseURL + "/api/device/credentials", deviceCredentials, DeviceCredentials.class).getBody();
     }
 
+    public Device createDevice(Device device) {
+        return restTemplate.postForEntity(baseURL + "/api/device", device, Device.class).getBody();
+    }
+
+    public Asset createAsset(Asset asset) {
+        return restTemplate.postForEntity(baseURL + "/api/asset", asset, Asset.class).getBody();
+    }
+
     public Asset createAsset(String name, String type) {
         Asset asset = new Asset();
         asset.setName(name);
@@ -147,6 +172,18 @@ public class RestClient implements ClientHttpRequestInterceptor {
         return restTemplate.postForEntity(baseURL + "/api/alarm", alarm, Alarm.class).getBody();
     }
 
+    public void deleteCustomer(CustomerId customerId) {
+        restTemplate.delete(baseURL + "/api/customer/{customerId}", customerId);
+    }
+
+    public void deleteDevice(DeviceId deviceId) {
+        restTemplate.delete(baseURL + "/api/device/{deviceId}", deviceId);
+    }
+
+    public void deleteAsset(AssetId assetId) {
+        restTemplate.delete(baseURL + "/api/asset/{assetId}", assetId);
+    }
+
     public Device assignDevice(CustomerId customerId, DeviceId deviceId) {
         return restTemplate.postForEntity(baseURL + "/api/customer/{customerId}/device/{deviceId}", null, Device.class,
                 customerId.toString(), deviceId.toString()).getBody();