diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index bcf3425f53..db83fd700a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -588,7 +588,7 @@ transport: # Edges parameters edges: rpc: - enabled: "${EDGES_RPC_ENABLED:false}" + enabled: "${EDGES_RPC_ENABLED:true}" port: "${EDGES_RPC_PORT:7070}" ssl: # Enable/disable SSL support diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index d24f38bc36..1602d24185 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -94,6 +94,11 @@ org.thingsboard rest-client + + org.thingsboard.common + edge-api + test + diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java index 647c8878b7..5a798a8cc8 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java @@ -31,7 +31,7 @@ import java.util.List; import java.util.Map; @RunWith(ClasspathSuite.class) -@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.msa.*Test"}) +@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.msa.*EdgeTest"}) public class ContainerTestSuite { private static DockerComposeContainer testContainer; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeImitator.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeImitator.java new file mode 100644 index 0000000000..823d180261 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeImitator.java @@ -0,0 +1,156 @@ +/** + * Copyright © 2016-2020 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.msa.edge; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.thingsboard.edge.rpc.EdgeGrpcClient; +import org.thingsboard.edge.rpc.EdgeRpcClient; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.DownlinkMsg; +import org.thingsboard.server.gen.edge.DownlinkResponseMsg; +import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.edge.EntityDataProto; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.UplinkResponseMsg; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Slf4j +public class EdgeImitator { + + private String routingKey; + private String routingSecret; + + private EdgeRpcClient edgeRpcClient; + + @Getter + private EdgeStorage storage; + + + public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException { + edgeRpcClient = new EdgeGrpcClient(); + storage = new EdgeStorage(); + this.routingKey = routingKey; + this.routingSecret = routingSecret; + setEdgeCredentials("rpcHost", host); + setEdgeCredentials("rpcPort", port); + } + + private void setEdgeCredentials(String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { + Field fieldToSet = edgeRpcClient.getClass().getDeclaredField(fieldName); + fieldToSet.setAccessible(true); + fieldToSet.set(edgeRpcClient, value); + fieldToSet.setAccessible(false); + } + + public void connect() { + edgeRpcClient.connect(routingKey, routingSecret, + this::onUplinkResponse, + this::onEdgeUpdate, + this::onDownlink, + this::onError); + } + + public void disconnect() throws InterruptedException { + edgeRpcClient.disconnect(); + } + + private void onUplinkResponse(UplinkResponseMsg msg) { + log.info("onUplinkResponse: {}", msg); + } + + private void onEdgeUpdate(EdgeConfiguration edgeConfiguration) { + storage.setConfiguration(edgeConfiguration); + } + + private void onDownlink(DownlinkMsg downlinkMsg) { + ListenableFuture> future = processDownlinkMsg(downlinkMsg); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(@Nullable List result) { + DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(true).build(); + edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); + } + + @Override + public void onFailure(Throwable t) { + DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build(); + edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg); + } + }, MoreExecutors.directExecutor()); + } + + private void onError(Exception e) { + log.error("Error during Edge lifecycle: ", e); + } + + private ListenableFuture> processDownlinkMsg(DownlinkMsg downlinkMsg) { + List> result = new ArrayList<>(); + if (downlinkMsg.getDeviceUpdateMsgList() != null && !downlinkMsg.getDeviceUpdateMsgList().isEmpty()) { + for (DeviceUpdateMsg deviceUpdateMsg: downlinkMsg.getDeviceUpdateMsgList()) { + result.add(storage.processEntity(deviceUpdateMsg.getMsgType(), EdgeEventType.DEVICE, new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()))); + } + } + if (downlinkMsg.getAssetUpdateMsgList() != null && !downlinkMsg.getAssetUpdateMsgList().isEmpty()) { + for (AssetUpdateMsg assetUpdateMsg: downlinkMsg.getAssetUpdateMsgList()) { + result.add(storage.processEntity(assetUpdateMsg.getMsgType(), EdgeEventType.ASSET, new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB()))); + } + } + if (downlinkMsg.getRuleChainUpdateMsgList() != null && !downlinkMsg.getRuleChainUpdateMsgList().isEmpty()) { + for (RuleChainUpdateMsg ruleChainUpdateMsg: downlinkMsg.getRuleChainUpdateMsgList()) { + result.add(storage.processEntity(ruleChainUpdateMsg.getMsgType(), EdgeEventType.RULE_CHAIN, new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()))); + } + } + if (downlinkMsg.getDashboardUpdateMsgList() != null && !downlinkMsg.getDashboardUpdateMsgList().isEmpty()) { + for (DashboardUpdateMsg dashboardUpdateMsg: downlinkMsg.getDashboardUpdateMsgList()) { + result.add(storage.processEntity(dashboardUpdateMsg.getMsgType(), EdgeEventType.DASHBOARD, new UUID(dashboardUpdateMsg.getIdMSB(), dashboardUpdateMsg.getIdLSB()))); + } + } + if (downlinkMsg.getRelationUpdateMsgList() != null && !downlinkMsg.getRelationUpdateMsgList().isEmpty()) { + for (RelationUpdateMsg relationUpdateMsg: downlinkMsg.getRelationUpdateMsgList()) { + result.add(storage.processRelation(relationUpdateMsg)); + } + } + if (downlinkMsg.getAlarmUpdateMsgList() != null && !downlinkMsg.getAlarmUpdateMsgList().isEmpty()) { + for (AlarmUpdateMsg alarmUpdateMsg: downlinkMsg.getAlarmUpdateMsgList()) { + result.add(storage.processAlarm(alarmUpdateMsg)); + } + } + if (downlinkMsg.getEntityDataList() != null && !downlinkMsg.getEntityDataList().isEmpty()) { + for (EntityDataProto entityDataProto: downlinkMsg.getEntityDataList()) { + if (entityDataProto.hasPostTelemetryMsg()) { + result.add(storage.processTelemetry(new UUID(entityDataProto.getEntityIdMSB(), entityDataProto.getEntityIdLSB()), entityDataProto.getPostTelemetryMsg())); + } + } + } + return Futures.allAsList(result); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeStorage.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeStorage.java new file mode 100644 index 0000000000..0bad7473cf --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeStorage.java @@ -0,0 +1,120 @@ +/** + * Copyright © 2016-2020 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.msa.edge; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.gen.edge.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.edge.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.UpdateMsgType; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Getter +@Setter +public class EdgeStorage { + + private EdgeConfiguration configuration; + + private Map entities; + private Map alarms; + private List relations; + private Map latestTelemetry; + + + public EdgeStorage() { + entities = new HashMap<>(); + alarms = new HashMap<>(); + relations = new ArrayList<>(); + latestTelemetry = new HashMap<>(); + } + + public ListenableFuture processEntity(UpdateMsgType msgType, EdgeEventType type, UUID uuid) { + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + entities.put(uuid, type); + break; + case ENTITY_DELETED_RPC_MESSAGE: + entities.remove(uuid); + break; + } + return Futures.immediateFuture(null); + } + + public ListenableFuture processRelation(RelationUpdateMsg relationMsg) { + EntityRelation relation = new EntityRelation(); + relation.setType(relationMsg.getType()); + relation.setTypeGroup(RelationTypeGroup.valueOf(relationMsg.getTypeGroup())); + relation.setTo(EntityIdFactory.getByTypeAndUuid(relationMsg.getToEntityType(), new UUID(relationMsg.getToIdMSB(), relationMsg.getToIdLSB()))); + relation.setFrom(EntityIdFactory.getByTypeAndUuid(relationMsg.getFromEntityType(), new UUID(relationMsg.getFromIdMSB(), relationMsg.getFromIdLSB()))); + switch (relationMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + relations.add(relation); + break; + case ENTITY_DELETED_RPC_MESSAGE: + relations.remove(relation); + break; + } + return Futures.immediateFuture(null); + } + + public ListenableFuture processAlarm(AlarmUpdateMsg alarmMsg) { + switch (alarmMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case ALARM_ACK_RPC_MESSAGE: + case ALARM_CLEAR_RPC_MESSAGE: + alarms.put(alarmMsg.getType(), AlarmStatus.valueOf(alarmMsg.getStatus())); + break; + case ENTITY_DELETED_RPC_MESSAGE: + alarms.remove(alarmMsg.getName()); + break; + } + return Futures.immediateFuture(null); + } + + public ListenableFuture processTelemetry(UUID uuid, TransportProtos.PostTelemetryMsg telemetryMsg) { + latestTelemetry.put(uuid, telemetryMsg); + return Futures.immediateFuture(null); + } + + public Set getEntitiesByType(EdgeEventType type) { + Map filtered = entities.entrySet().stream() + .filter(entry -> entry.getValue().equals(type)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + return filtered.keySet(); + } + +} diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeTest.java new file mode 100644 index 0000000000..a1f8c8e374 --- /dev/null +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/edge/EdgeTest.java @@ -0,0 +1,341 @@ +/** + * Copyright © 2016-2020 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.msa.edge; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.junit.*; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.EdgeConfiguration; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.msa.AbstractContainerTest; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +@Slf4j +public class EdgeTest extends AbstractContainerTest { + + private static EdgeImitator edgeImitator; + + @BeforeClass + public static void init() throws NoSuchFieldException, IllegalAccessException, InterruptedException, IOException { + restClient.login("tenant@thingsboard.org", "tenant"); + installation(); + edgeImitator = new EdgeImitator("localhost", 7070, "routing", "secret"); + edgeImitator.connect(); + Thread.sleep(10000); + } + + @Test + public void testReceivedData() { + Edge edge = restClient.getTenantEdge("Edge1").get(); + + EdgeConfiguration configuration = edgeImitator.getStorage().getConfiguration(); + Assert.assertNotNull(configuration); + + Map entities = edgeImitator.getStorage().getEntities(); + Assert.assertFalse(entities.isEmpty()); + + Set devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); + Assert.assertEquals(1, devices.size()); + for (Device device: restClient.getEdgeDevices(edge.getId(), new TextPageLink(1)).getData()) { + Assert.assertTrue(devices.contains(device.getUuidId())); + } + + Set ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); + Assert.assertEquals(1, ruleChains.size()); + for (RuleChain ruleChain: restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(1)).getData()) { + Assert.assertTrue(ruleChains.contains(ruleChain.getUuidId())); + } + + Set assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); + Assert.assertEquals(1, assets.size()); + for (Asset asset: restClient.getEdgeAssets(edge.getId(), new TextPageLink(1)).getData()) { + Assert.assertTrue(assets.contains(asset.getUuidId())); + } + } + + @Test + public void testDevices() throws Exception { + Edge edge = restClient.getTenantEdge("Edge1").get(); + + Device device = new Device(); + device.setName("Edge Device 2"); + device.setType("test"); + Device savedDevice = restClient.saveDevice(device); + restClient.assignDeviceToEdge(edge.getId(), savedDevice.getId()); + + Thread.sleep(1000); + Assert.assertTrue(restClient.getEdgeDevices(edge.getId(), new TextPageLink(2)).getData().contains(savedDevice)); + Set devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); + Assert.assertEquals(2, devices.size()); + Assert.assertTrue(devices.contains(savedDevice.getUuidId())); + + restClient.unassignDeviceFromEdge(edge.getId(), savedDevice.getId()); + Thread.sleep(1000); + Assert.assertFalse(restClient.getEdgeDevices(edge.getId(), new TextPageLink(2)).getData().contains(savedDevice)); + devices = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DEVICE); + Assert.assertEquals(1, devices.size()); + Assert.assertFalse(devices.contains(savedDevice.getUuidId())); + + restClient.deleteDevice(savedDevice.getId()); + } + + @Test + public void testAssets() throws Exception { + Edge edge = restClient.getTenantEdge("Edge1").get(); + + Asset asset = new Asset(); + asset.setName("Edge Asset 2"); + asset.setType("test"); + Asset savedAsset = restClient.saveAsset(asset); + restClient.assignAssetToEdge(edge.getId(), savedAsset.getId()); + + Thread.sleep(1000); + Assert.assertTrue(restClient.getEdgeAssets(edge.getId(), new TextPageLink(2)).getData().contains(savedAsset)); + Set assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); + Assert.assertEquals(2, assets.size()); + Assert.assertTrue(assets.contains(savedAsset.getUuidId())); + + restClient.unassignAssetFromEdge(edge.getId(), savedAsset.getId()); + Thread.sleep(1000); + Assert.assertFalse(restClient.getEdgeAssets(edge.getId(), new TextPageLink(2)).getData().contains(savedAsset)); + assets = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.ASSET); + Assert.assertEquals(1, assets.size()); + Assert.assertFalse(assets.contains(savedAsset.getUuidId())); + + restClient.deleteAsset(savedAsset.getId()); + } + + @Test + public void testRuleChains() throws Exception { + Edge edge = restClient.getTenantEdge("Edge1").get(); + + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("Edge Test Rule Chain"); + ruleChain.setType(RuleChainType.EDGE); + RuleChain savedRuleChain = restClient.saveRuleChain(ruleChain); + restClient.assignRuleChainToEdge(edge.getId(), savedRuleChain.getId()); + + Thread.sleep(1000); + Assert.assertTrue(restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(2)).getData().contains(savedRuleChain)); + Set ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); + Assert.assertEquals(2, ruleChains.size()); + Assert.assertTrue(ruleChains.contains(savedRuleChain.getUuidId())); + + restClient.unassignRuleChainFromEdge(edge.getId(), savedRuleChain.getId()); + Thread.sleep(1000); + Assert.assertFalse(restClient.getEdgeRuleChains(edge.getId(), new TimePageLink(2)).getData().contains(savedRuleChain)); + ruleChains = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.RULE_CHAIN); + Assert.assertEquals(1, ruleChains.size()); + Assert.assertFalse(ruleChains.contains(savedRuleChain.getUuidId())); + + restClient.deleteRuleChain(savedRuleChain.getId()); + + } + + @Test + public void testDashboards() throws Exception { + Edge edge = restClient.getTenantEdge("Edge1").get(); + + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("Edge Test Dashboard"); + Dashboard savedDashboard = restClient.saveDashboard(dashboard); + restClient.assignDashboardToEdge(edge.getId(), savedDashboard.getId()); + + Thread.sleep(1000); + Assert.assertTrue(restClient.getEdgeDashboards(edge.getId(), new TimePageLink(2)).getData().stream().allMatch(dashboardInfo -> dashboardInfo.getUuidId().equals(savedDashboard.getUuidId()))); + Set dashboards = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DASHBOARD); + Assert.assertEquals(1, dashboards.size()); + Assert.assertTrue(dashboards.contains(savedDashboard.getUuidId())); + + restClient.unassignDashboardFromEdge(edge.getId(), savedDashboard.getId()); + Thread.sleep(1000); + Assert.assertFalse(restClient.getEdgeDashboards(edge.getId(), new TimePageLink(2)).getData().stream().anyMatch(dashboardInfo -> dashboardInfo.getUuidId().equals(savedDashboard.getUuidId()))); + dashboards = edgeImitator.getStorage().getEntitiesByType(EdgeEventType.DASHBOARD); + Assert.assertEquals(0, dashboards.size()); + Assert.assertFalse(dashboards.contains(savedDashboard.getUuidId())); + + restClient.deleteDashboard(savedDashboard.getId()); + + } + + @Test + public void testRelations() throws InterruptedException { + Device device = restClient.getTenantDevice("Edge Device 1").get(); + Asset asset = restClient.getTenantAsset("Edge Asset 1").get(); + + EntityRelation relation = new EntityRelation(); + relation.setType("test"); + relation.setFrom(device.getId()); + relation.setTo(asset.getId()); + relation.setTypeGroup(RelationTypeGroup.COMMON); + restClient.saveRelation(relation); + + Thread.sleep(1000); + List relations = edgeImitator.getStorage().getRelations(); + Assert.assertEquals(1, relations.size()); + Assert.assertTrue(relations.contains(relation)); + restClient.deleteRelation(relation.getFrom(), relation.getType(), relation.getTypeGroup(), relation.getTo()); + + Thread.sleep(1000); + relations = edgeImitator.getStorage().getRelations(); + Assert.assertEquals(0, relations.size()); + Assert.assertFalse(relations.contains(relation)); + } + + @Test + public void testAlarms() throws Exception { + Device device = restClient.getTenantDevice("Edge Device 1").get(); + Alarm alarm = new Alarm(); + alarm.setOriginator(device.getId()); + alarm.setStatus(AlarmStatus.ACTIVE_UNACK); + alarm.setType("alarm"); + alarm.setSeverity(AlarmSeverity.CRITICAL); + + Alarm savedAlarm = restClient.saveAlarm(alarm); + AlarmInfo alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); + Thread.sleep(1000); + + Assert.assertEquals(1, edgeImitator.getStorage().getAlarms().size()); + Assert.assertTrue(edgeImitator.getStorage().getAlarms().containsKey(alarmInfo.getType())); + Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); + restClient.ackAlarm(savedAlarm.getId()); + + Thread.sleep(1000); + alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); + Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isAck()); + Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); + restClient.clearAlarm(savedAlarm.getId()); + + Thread.sleep(1000); + alarmInfo = restClient.getAlarmInfoById(savedAlarm.getId()).get(); + Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isAck()); + Assert.assertTrue(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()).isCleared()); + Assert.assertEquals(edgeImitator.getStorage().getAlarms().get(alarmInfo.getType()), alarmInfo.getStatus()); + + restClient.deleteAlarm(savedAlarm.getId()); + + } + + @Ignore + @Test + public void testTelemetry() throws Exception { + Device device = restClient.getTenantDevice("Edge Device 1").get(); + DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get(); + ResponseEntity response = restClient.getRestTemplate() + .postForEntity(HTTPS_URL + "/api/v1/{credentialsId}/telemetry", + "{'test': 25}", + ResponseEntity.class, + deviceCredentials.getCredentialsId()); + Assert.assertEquals(response.getStatusCode(), HttpStatus.OK); + Thread.sleep(1000); + List keys = restClient.getTimeseriesKeys(device.getId()); + List latestTimeseries = restClient.getLatestTimeseries(device.getId(), keys); + Assert.assertEquals(1, latestTimeseries.size()); + TsKvEntry tsKvEntry = latestTimeseries.get(0); + Map telemetry = edgeImitator.getStorage().getLatestTelemetry(); + Assert.assertEquals(1, telemetry.size()); + Assert.assertTrue(telemetry.containsKey(device.getUuidId())); + TransportProtos.PostTelemetryMsg telemetryMsg = telemetry.get(device.getUuidId()); + Assert.assertEquals(1, telemetryMsg.getTsKvListCount()); + TransportProtos.TsKvListProto tsKv = telemetryMsg.getTsKvListList().get(0); + Assert.assertEquals(tsKvEntry.getTs(), tsKv.getTs()); + Assert.assertEquals(1, tsKv.getKvCount()); + TransportProtos.KeyValueProto keyValue = tsKv.getKvList().get(0); + Assert.assertEquals(tsKvEntry.getKey(), keyValue.getKey()); + Assert.assertEquals(tsKvEntry.getValueAsString(), Long.toString(keyValue.getLongV())); + } + + @AfterClass + public static void destroy() throws InterruptedException { + uninstallation(); + edgeImitator.disconnect(); + } + + private static void installation() throws IOException { + Edge edge = new Edge(); + edge.setName("Edge1"); + edge.setType("test"); + edge.setRoutingKey("routing"); + edge.setSecret("secret"); + Edge savedEdge = restClient.saveEdge(edge); + + Device device = new Device(); + device.setName("Edge Device 1"); + device.setType("test"); + Device savedDevice = restClient.saveDevice(device); + restClient.assignDeviceToEdge(savedEdge.getId(), savedDevice.getId()); + + Asset asset = new Asset(); + asset.setName("Edge Asset 1"); + asset.setType("test"); + Asset savedAsset = restClient.saveAsset(asset); + restClient.assignAssetToEdge(savedEdge.getId(), savedAsset.getId()); + + ObjectMapper mapper = new ObjectMapper(); + Class edgeTestClass = EdgeTest.class; + JsonNode configuration = mapper.readTree(edgeTestClass.getClassLoader().getResourceAsStream("RootRuleChain.json")); + RuleChain ruleChain = mapper.treeToValue(configuration.get("ruleChain"), RuleChain.class); + RuleChainMetaData ruleChainMetaData = mapper.treeToValue(configuration.get("metadata"), RuleChainMetaData.class); + RuleChain savedRuleChain = restClient.saveRuleChain(ruleChain); + ruleChainMetaData.setRuleChainId(savedRuleChain.getId()); + restClient.saveRuleChainMetaData(ruleChainMetaData); + restClient.setRootRuleChain(savedRuleChain.getId()); + } + + private static void uninstallation() { + Device device = restClient.getTenantDevice("Edge Device 1").get(); + restClient.deleteDevice(device.getId()); + + Asset asset = restClient.getTenantAsset("Edge Asset 1").get(); + restClient.deleteAsset(asset.getId()); + + Edge edge = restClient.getTenantEdge("Edge1").get(); + restClient.deleteEdge(edge.getId()); + + List ruleChains = restClient.getRuleChains(new TextPageLink(3)).getData(); + RuleChain oldRoot = ruleChains.stream().filter(ruleChain -> ruleChain.getName().equals("Root Rule Chain")).findAny().get(); + RuleChain newRoot = ruleChains.stream().filter(ruleChain -> ruleChain.getName().equals("Test Root Rule Chain")).findAny().get(); + restClient.setRootRuleChain(oldRoot.getId()); + restClient.deleteRuleChain(newRoot.getId()); + } + +} diff --git a/msa/black-box-tests/src/test/resources/RootRuleChain.json b/msa/black-box-tests/src/test/resources/RootRuleChain.json new file mode 100644 index 0000000000..56324e72f5 --- /dev/null +++ b/msa/black-box-tests/src/test/resources/RootRuleChain.json @@ -0,0 +1,133 @@ +{ + "ruleChain": { + "additionalInfo": null, + "name": "Test Root Rule Chain", + "type": "CORE", + "firstRuleNodeId": null, + "root": false, + "debugMode": false, + "configuration": null + }, + "metadata": { + "firstNodeIndex": 4, + "nodes": [ + { + "additionalInfo": { + "layoutX": 1117, + "layoutY": 156 + }, + "type": "org.thingsboard.rule.engine.edge.TbMsgPushToEdgeNode", + "name": "Push to edge", + "debugMode": false, + "configuration": { + "version": 0 + } + }, + { + "additionalInfo": { + "layoutX": 825, + "layoutY": 407 + }, + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", + "name": "RPC Call Request", + "debugMode": false, + "configuration": { + "timeoutInSeconds": 60 + } + }, + { + "additionalInfo": { + "layoutX": 826, + "layoutY": 327 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log Other", + "debugMode": false, + "configuration": { + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + } + }, + { + "additionalInfo": { + "layoutX": 827, + "layoutY": 244 + }, + "type": "org.thingsboard.rule.engine.action.TbLogNode", + "name": "Log RPC from Device", + "debugMode": false, + "configuration": { + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" + } + }, + { + "additionalInfo": { + "layoutX": 347, + "layoutY": 149 + }, + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", + "name": "Message Type Switch", + "debugMode": false, + "configuration": { + "version": 0 + } + }, + { + "additionalInfo": { + "layoutX": 821, + "layoutY": 72 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", + "name": "Save Client Attributes", + "debugMode": false, + "configuration": { + "scope": "CLIENT_SCOPE" + } + }, + { + "additionalInfo": { + "layoutX": 824, + "layoutY": 156 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries", + "debugMode": false, + "configuration": { + "defaultTTL": 0 + } + } + ], + "connections": [ + { + "fromIndex": 4, + "toIndex": 1, + "type": "RPC Request to Device" + }, + { + "fromIndex": 4, + "toIndex": 3, + "type": "RPC Request from Device" + }, + { + "fromIndex": 4, + "toIndex": 6, + "type": "Post telemetry" + }, + { + "fromIndex": 4, + "toIndex": 5, + "type": "Post attributes" + }, + { + "fromIndex": 4, + "toIndex": 2, + "type": "Other" + }, + { + "fromIndex": 6, + "toIndex": 0, + "type": "Success" + } + ], + "ruleChainConnections": null + } +} \ No newline at end of file