|
|
|
@ -15,20 +15,21 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.rule.engine.metadata; |
|
|
|
|
|
|
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
|
|
|
import com.fasterxml.jackson.databind.JsonNode; |
|
|
|
import com.google.common.util.concurrent.Futures; |
|
|
|
import org.junit.jupiter.api.AfterEach; |
|
|
|
import org.junit.jupiter.api.BeforeEach; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.junit.jupiter.api.Assertions; |
|
|
|
import org.junit.jupiter.api.extension.ExtendWith; |
|
|
|
import org.junit.jupiter.params.provider.Arguments; |
|
|
|
import org.mockito.ArgumentCaptor; |
|
|
|
import org.mockito.Mock; |
|
|
|
import org.mockito.Spy; |
|
|
|
import org.mockito.junit.jupiter.MockitoExtension; |
|
|
|
import org.thingsboard.common.util.AbstractListeningExecutor; |
|
|
|
import org.thingsboard.common.util.JacksonUtil; |
|
|
|
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; |
|
|
|
import org.thingsboard.rule.engine.api.TbContext; |
|
|
|
import org.thingsboard.rule.engine.api.TbNode; |
|
|
|
import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.api.TbNodeException; |
|
|
|
import org.thingsboard.rule.engine.util.TbMsgSource; |
|
|
|
@ -43,7 +44,6 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.StringDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.TsKvEntry; |
|
|
|
import org.thingsboard.server.common.data.msg.TbMsgType; |
|
|
|
import org.thingsboard.server.common.data.util.TbPair; |
|
|
|
import org.thingsboard.server.common.msg.TbMsg; |
|
|
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|
|
|
import org.thingsboard.server.dao.attributes.AttributesService; |
|
|
|
@ -51,25 +51,26 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertNotNull; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertThrows; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
import static org.mockito.Mockito.lenient; |
|
|
|
import static org.mockito.Mockito.never; |
|
|
|
import static org.mockito.Mockito.timeout; |
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
import static org.mockito.Mockito.when; |
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class) |
|
|
|
public class TbGetAttributesNodeTest { |
|
|
|
public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest { |
|
|
|
|
|
|
|
private static final EntityId ORIGINATOR = new DeviceId(Uuids.timeBased()); |
|
|
|
private static final TenantId TENANT_ID = TenantId.fromUUID(Uuids.timeBased()); |
|
|
|
private final EntityId ORIGINATOR_ID = new DeviceId(UUID.fromString("965f2975-787a-4f21-87e6-9aa4738186ff")); |
|
|
|
private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("befd3239-79b8-4263-a8d1-95b69f44f798")); |
|
|
|
private AbstractListeningExecutor dbExecutor; |
|
|
|
|
|
|
|
@Mock |
|
|
|
@ -84,6 +85,8 @@ public class TbGetAttributesNodeTest { |
|
|
|
private List<String> sharedAttributes; |
|
|
|
private List<String> tsKeys; |
|
|
|
private long ts; |
|
|
|
|
|
|
|
@Spy |
|
|
|
private TbGetAttributesNode node; |
|
|
|
|
|
|
|
@BeforeEach |
|
|
|
@ -107,16 +110,16 @@ public class TbGetAttributesNodeTest { |
|
|
|
tsKeys = List.of("temperature", "humidity", "unknown"); |
|
|
|
ts = System.currentTimeMillis(); |
|
|
|
|
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.CLIENT_SCOPE, clientAttributes)) |
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.CLIENT_SCOPE, clientAttributes)) |
|
|
|
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts))); |
|
|
|
|
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SERVER_SCOPE, serverAttributes)) |
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.SERVER_SCOPE, serverAttributes)) |
|
|
|
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts))); |
|
|
|
|
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SHARED_SCOPE, sharedAttributes)) |
|
|
|
lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.SHARED_SCOPE, sharedAttributes)) |
|
|
|
.thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts))); |
|
|
|
|
|
|
|
lenient().when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR, tsKeys)) |
|
|
|
lenient().when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR_ID, tsKeys)) |
|
|
|
.thenReturn(Futures.immediateFuture(getListTsKvEntry(tsKeys, ts))); |
|
|
|
} |
|
|
|
|
|
|
|
@ -129,7 +132,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.METADATA, false, false); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -148,7 +151,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchLatestTimeseriesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.METADATA, true, false); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -167,7 +170,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchAttributesToData_whenOnMsg_thenShouldTellSuccess() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.DATA, false, false); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -186,7 +189,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellSuccess() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.DATA, true, false); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -205,7 +208,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellFailure() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.METADATA, false, true); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -224,7 +227,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellFailure() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.DATA, true, true); |
|
|
|
var msg = getTbMsg(ORIGINATOR); |
|
|
|
var msg = getTbMsg(ORIGINATOR_ID); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
@ -243,7 +246,7 @@ public class TbGetAttributesNodeTest { |
|
|
|
public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception { |
|
|
|
// GIVEN
|
|
|
|
node = initNode(TbMsgSource.DATA, true, true); |
|
|
|
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); |
|
|
|
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); |
|
|
|
@ -253,56 +256,6 @@ public class TbGetAttributesNodeTest { |
|
|
|
assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { |
|
|
|
var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); |
|
|
|
var node = new TbGetAttributesNode(); |
|
|
|
String oldConfig = "{\"fetchToData\":false," + |
|
|
|
"\"clientAttributeNames\":[]," + |
|
|
|
"\"sharedAttributeNames\":[]," + |
|
|
|
"\"serverAttributeNames\":[]," + |
|
|
|
"\"latestTsKeyNames\":[]," + |
|
|
|
"\"tellFailureIfAbsent\":true," + |
|
|
|
"\"getLatestValueWithTs\":false}"; |
|
|
|
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); |
|
|
|
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson); |
|
|
|
Assertions.assertTrue(upgrade.getFirst()); |
|
|
|
Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void givenOldConfigWithNoFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { |
|
|
|
var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); |
|
|
|
var node = new TbGetAttributesNode(); |
|
|
|
String oldConfig = "{\"clientAttributeNames\":[]," + |
|
|
|
"\"sharedAttributeNames\":[]," + |
|
|
|
"\"serverAttributeNames\":[]," + |
|
|
|
"\"latestTsKeyNames\":[]," + |
|
|
|
"\"tellFailureIfAbsent\":true," + |
|
|
|
"\"getLatestValueWithTs\":false}"; |
|
|
|
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); |
|
|
|
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson); |
|
|
|
Assertions.assertTrue(upgrade.getFirst()); |
|
|
|
Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void givenOldConfigWithNullFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { |
|
|
|
var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); |
|
|
|
var node = new TbGetAttributesNode(); |
|
|
|
String oldConfig = "{\"fetchToData\":null," + |
|
|
|
"\"clientAttributeNames\":[]," + |
|
|
|
"\"sharedAttributeNames\":[]," + |
|
|
|
"\"serverAttributeNames\":[]," + |
|
|
|
"\"latestTsKeyNames\":[]," + |
|
|
|
"\"tellFailureIfAbsent\":true," + |
|
|
|
"\"getLatestValueWithTs\":false}"; |
|
|
|
JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); |
|
|
|
TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson); |
|
|
|
Assertions.assertTrue(upgrade.getFirst()); |
|
|
|
Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); |
|
|
|
} |
|
|
|
|
|
|
|
private TbMsg checkMsg(boolean checkSuccess) { |
|
|
|
var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); |
|
|
|
if (checkSuccess) { |
|
|
|
@ -421,4 +374,117 @@ public class TbGetAttributesNodeTest { |
|
|
|
return kvEntriesList; |
|
|
|
} |
|
|
|
|
|
|
|
private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { |
|
|
|
return Stream.of( |
|
|
|
// config for version 1 with upgrade from version 0
|
|
|
|
Arguments.of(0, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"fetchToData":false, |
|
|
|
"clientAttributeNames":[], |
|
|
|
"sharedAttributeNames":[], |
|
|
|
"serverAttributeNames":[], |
|
|
|
"latestTsKeyNames":[], |
|
|
|
"tellFailureIfAbsent":true, |
|
|
|
"getLatestValueWithTs":false |
|
|
|
} |
|
|
|
""", |
|
|
|
true, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"tellFailureIfAbsent": true, |
|
|
|
"fetchTo": "METADATA", |
|
|
|
"clientAttributeNames": [], |
|
|
|
"sharedAttributeNames": [], |
|
|
|
"serverAttributeNames": [], |
|
|
|
"latestTsKeyNames": [], |
|
|
|
"getLatestValueWithTs": false |
|
|
|
} |
|
|
|
""" |
|
|
|
), |
|
|
|
// config for version 1 with upgrade from version 0 (old config with no fetchToData property)
|
|
|
|
Arguments.of(0, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"clientAttributeNames":[], |
|
|
|
"sharedAttributeNames":[],"serverAttributeNames":[], |
|
|
|
"latestTsKeyNames":[], |
|
|
|
"tellFailureIfAbsent":true, |
|
|
|
"getLatestValueWithTs":false |
|
|
|
} |
|
|
|
""", |
|
|
|
true, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"tellFailureIfAbsent": true, |
|
|
|
"fetchTo": "METADATA", |
|
|
|
"clientAttributeNames": [], |
|
|
|
"sharedAttributeNames": [], |
|
|
|
"serverAttributeNames": [], |
|
|
|
"latestTsKeyNames": [], |
|
|
|
"getLatestValueWithTs": false |
|
|
|
} |
|
|
|
""" |
|
|
|
), |
|
|
|
// config for version 1 with upgrade from version 0 (old config with null fetchToData property)
|
|
|
|
Arguments.of(0, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"fetchToData":null, |
|
|
|
"clientAttributeNames":[], |
|
|
|
"sharedAttributeNames":[], |
|
|
|
"serverAttributeNames":[], |
|
|
|
"latestTsKeyNames":[], |
|
|
|
"tellFailureIfAbsent":true, |
|
|
|
"getLatestValueWithTs":false |
|
|
|
} |
|
|
|
""", |
|
|
|
true, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"tellFailureIfAbsent": true, |
|
|
|
"fetchTo": "METADATA", |
|
|
|
"clientAttributeNames": [], |
|
|
|
"sharedAttributeNames": [], |
|
|
|
"serverAttributeNames": [], |
|
|
|
"latestTsKeyNames": [], |
|
|
|
"getLatestValueWithTs": false |
|
|
|
} |
|
|
|
""" |
|
|
|
), |
|
|
|
// config for version 1 with upgrade from version 1
|
|
|
|
Arguments.of(1, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"tellFailureIfAbsent": true, |
|
|
|
"fetchTo": "METADATA", |
|
|
|
"clientAttributeNames": [], |
|
|
|
"sharedAttributeNames": [], |
|
|
|
"serverAttributeNames": [], |
|
|
|
"latestTsKeyNames": [], |
|
|
|
"getLatestValueWithTs": false |
|
|
|
} |
|
|
|
""", |
|
|
|
false, |
|
|
|
""" |
|
|
|
{ |
|
|
|
"tellFailureIfAbsent": true, |
|
|
|
"fetchTo": "METADATA", |
|
|
|
"clientAttributeNames": [], |
|
|
|
"sharedAttributeNames": [], |
|
|
|
"serverAttributeNames": [], |
|
|
|
"latestTsKeyNames": [], |
|
|
|
"getLatestValueWithTs": false |
|
|
|
} |
|
|
|
""" |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected TbNode getTestNode() { |
|
|
|
return node; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|