refactoring & added tests for alarm status filter node & entity and msg type switch nodes & check field presence

This commit is contained in:
ShvaykaD 2023-06-28 12:43:49 +03:00
parent 3478b020ef
commit 0c4c8353de
11 changed files with 413 additions and 25 deletions

View File

@ -852,17 +852,10 @@ class DefaultTbContext implements TbContext {
}
private static String getFailureMessage(Throwable th) {
String failureMessage;
if (th != null) {
if (!StringUtils.isEmpty(th.getMessage())) {
failureMessage = th.getMessage();
} else {
failureMessage = th.getClass().getSimpleName();
}
} else {
failureMessage = null;
if (th == null) {
return null;
}
return failureMessage;
return StringUtils.isNotEmpty(th.getMessage()) ? th.getMessage() : th.getClass().getSimpleName();
}
private class SimpleTbQueueCallback implements TbQueueCallback {

View File

@ -114,13 +114,14 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE;
@Service
@Profile("install")
@Slf4j
public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
public static final String CUSTOMER_CRED = "customer";
public static final String DEFAULT_DEVICE_TYPE = "default";
public static final String ACTIVITY_STATE = "active";
@Autowired

View File

@ -65,6 +65,7 @@ public class DataConstants {
public static final String PROVISION_KEY = "provisionDeviceKey";
public static final String PROVISION_SECRET = "provisionDeviceSecret";
public static final String DEFAULT_DEVICE_TYPE = "default";
public static final String DEVICE_NAME = "deviceName";
public static final String DEVICE_TYPE = "deviceType";
public static final String CERT_PUB_KEY = "x509CertPubKey";

View File

@ -42,7 +42,7 @@ public class StringUtils {
}
public static boolean isNotEmpty(String source) {
return source != null && !source.isEmpty();
return !isEmpty(source);
}
public static boolean isNotBlank(String source) {

View File

@ -71,6 +71,7 @@ import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE;
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED;
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN;
import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG;
@ -85,7 +86,6 @@ import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMess
@Slf4j
public abstract class AbstractGatewaySessionHandler<T extends AbstractGatewayDeviceSessionContext> {
protected static final String DEFAULT_DEVICE_TYPE = "default";
private static final String CAN_T_PARSE_VALUE = "Can't parse value: ";
private static final String DEVICE_PROPERTY = "device";

View File

@ -38,17 +38,9 @@ public abstract class TbAbstractExternalNode implements TbNode {
protected void tellFailure(TbContext ctx, TbMsg tbMsg, Throwable t) {
if (forceAck) {
if (t == null) {
ctx.enqueueForTellNext(tbMsg.copyWithNewCtx(), TbNodeConnectionType.FAILURE);
} else {
ctx.enqueueForTellFailure(tbMsg.copyWithNewCtx(), t);
}
ctx.enqueueForTellFailure(tbMsg.copyWithNewCtx(), t);
} else {
if (t == null) {
ctx.tellNext(tbMsg, TbNodeConnectionType.FAILURE);
} else {
ctx.tellFailure(tbMsg, t);
}
ctx.tellFailure(tbMsg, t);
}
}

View File

@ -124,4 +124,5 @@ class TbAssetTypeSwitchNodeTest {
private TbMsg getTbMsg(EntityId entityId) {
return TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), entityId, new TbMsgMetaData(), "{}", callback);
}
}

View File

@ -163,5 +163,4 @@ class TbCheckAlarmStatusNodeTest {
return TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), entityId, new TbMsgMetaData(), msgData);
}
}

View File

@ -0,0 +1,206 @@
/**
* Copyright © 2016-2023 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.rule.engine.filter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.msg.TbNodeConnectionType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE;
import static org.thingsboard.server.common.data.DataConstants.DEVICE_NAME;
import static org.thingsboard.server.common.data.DataConstants.DEVICE_TYPE;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST;
class TbCheckMessageNodeTest {
private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID());
private static final TbMsgMetaData EMPTY_METADATA = new TbMsgMetaData();
private static final String EMPTY_DATA = "{}";
private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), DEVICE_ID, EMPTY_METADATA, EMPTY_DATA);
private static TbCheckMessageNode node;
private static TbContext ctx;
@BeforeEach
void setUp() {
ctx = mock(TbContext.class);
node = new TbCheckMessageNode();
}
@AfterEach
void tearDown() {
node.destroy();
}
@Test
void givenDefaultConfig_whenOnMsg_then_True() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
// WHEN
node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
}
@Test
void givenCustomConfigWithoutCheckAllKeysAndWithEmptyLists_whenOnMsg_then_False() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
configuration.setCheckAllKeys(false);
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
// WHEN
node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG);
}
@Test
void givenCustomConfigWithCheckAllKeys_whenOnMsg_then_True() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
configuration.setMessageNames(List.of("temperature-0"));
configuration.setMetadataNames(List.of("deviceName", "deviceType", "ts"));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
TbMsg tbMsg = getTbMsg();
// WHEN
node.onMsg(ctx, tbMsg);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(tbMsg);
}
@Test
void givenCustomConfigWithCheckAllKeys_whenOnMsg_then_False() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
configuration.setMessageNames(List.of("temperature-0", "temperature-1"));
configuration.setMetadataNames(List.of("deviceName", "deviceType", "ts"));
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
TbMsg tbMsg = getTbMsg();
// WHEN
node.onMsg(ctx, tbMsg);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(tbMsg);
}
@Test
void givenCustomConfigWithoutCheckAllKeys_whenOnMsg_then_True() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
configuration.setMessageNames(List.of("temperature-0", "temperature-1"));
configuration.setCheckAllKeys(false);
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
TbMsg tbMsg = getTbMsg();
// WHEN
node.onMsg(ctx, tbMsg);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(tbMsg);
}
@Test
void givenCustomConfigWithoutCheckAllKeysAndEmptyMsg_whenOnMsg_then_False() throws TbNodeException {
// GIVEN
var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration();
configuration.setMessageNames(List.of("temperature-0", "temperature-1"));
configuration.setCheckAllKeys(false);
node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration)));
TbMsg tbMsg = getTbMsg(true);
// WHEN
node.onMsg(ctx, tbMsg);
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE));
verify(ctx, never()).tellFailure(any(), any());
TbMsg newMsg = newMsgCaptor.getValue();
assertThat(newMsg).isNotNull();
assertThat(newMsg).isSameAs(tbMsg);
}
private TbMsg getTbMsg() {
return getTbMsg(false);
}
private TbMsg getTbMsg(boolean emptyData) {
String data = emptyData ? EMPTY_DATA : "{\"temperature-0\": 25}";
var metadata = new TbMsgMetaData();
metadata.putValue(DEVICE_NAME, "Test Device");
metadata.putValue(DEVICE_TYPE, DEFAULT_DEVICE_TYPE);
metadata.putValue("ts", String.valueOf(System.currentTimeMillis()));
return TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), DEVICE_ID, metadata, data);
}
}

View File

@ -0,0 +1,97 @@
/**
* Copyright © 2016-2023 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.rule.engine.filter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
class TbMsgTypeSwitchNodeTest {
private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID());
private static final TbMsgMetaData EMPTY_METADATA = new TbMsgMetaData();
private static final String EMPTY_DATA = "{}";
private static TbMsgTypeSwitchNode node;
private static TbContext ctx;
@BeforeEach
void setUp() {
ctx = mock(TbContext.class);
node = new TbMsgTypeSwitchNode();
}
@AfterEach
void tearDown() {
node.destroy();
}
@Test
void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException {
// GIVEN
List<TbMsg> tbMsgList = new ArrayList<>();
var tbMsgTypes = TbMsgType.values();
for (var msgType : tbMsgTypes) {
tbMsgList.add(getTbMsg(msgType));
}
// WHEN
for (TbMsg tbMsg : tbMsgList) {
node.onMsg(ctx, tbMsg);
}
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<String> nodeConnectionCapture = ArgumentCaptor.forClass(String.class);
verify(ctx, times(tbMsgList.size())).tellNext(newMsgCaptor.capture(), nodeConnectionCapture.capture());
verify(ctx, never()).tellFailure(any(), any());
var resultMsgs = newMsgCaptor.getAllValues();
var resultNodeConnections = nodeConnectionCapture.getAllValues();
for (int i = 0; i < resultMsgs.size(); i++) {
var msg = resultMsgs.get(i);
assertThat(msg).isNotNull();
assertThat(msg.getType()).isNotNull();
assertThat(msg).isSameAs(tbMsgList.get(i));
// todo add additional validation that types like ALARM or PROVISION returns OTHER for backward-compatibility.
assertThat(resultNodeConnections.get(i))
.isEqualTo(TbMsgType.getRuleNodeConnection(msg.getType()));
}
}
private TbMsg getTbMsg(TbMsgType msgType) {
return TbMsg.newMsg(msgType.name(), DEVICE_ID, EMPTY_METADATA, EMPTY_DATA);
}
}

View File

@ -0,0 +1,98 @@
/**
* Copyright © 2016-2023 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.rule.engine.filter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST;
class TbOriginatorTypeSwitchNodeTest {
private static final UUID RANDOM_UUID = UUID.randomUUID();
private static final TbMsgMetaData EMPTY_METADATA = new TbMsgMetaData();
private static final String EMPTY_DATA = "{}";
private static TbOriginatorTypeSwitchNode node;
private static TbContext ctx;
@BeforeEach
void setUp() {
ctx = mock(TbContext.class);
node = new TbOriginatorTypeSwitchNode();
}
@AfterEach
void tearDown() {
node.destroy();
}
@Test
void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException {
// GIVEN
List<TbMsg> tbMsgList = new ArrayList<>();
var entityTypes = EntityType.values();
for (var entityType : entityTypes) {
var entityId = EntityIdFactory.getByTypeAndUuid(entityType, RANDOM_UUID);
tbMsgList.add(getTbMsg(entityId));
}
// WHEN
for (TbMsg tbMsg : tbMsgList) {
node.onMsg(ctx, tbMsg);
}
// THEN
ArgumentCaptor<TbMsg> newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
ArgumentCaptor<String> nodeConnectionCapture = ArgumentCaptor.forClass(String.class);
verify(ctx, times(tbMsgList.size())).tellNext(newMsgCaptor.capture(), nodeConnectionCapture.capture());
verify(ctx, never()).tellFailure(any(), any());
var resultMsgs = newMsgCaptor.getAllValues();
var resultNodeConnections = nodeConnectionCapture.getAllValues();
for (int i = 0; i < resultMsgs.size(); i++) {
var msg = resultMsgs.get(i);
assertThat(msg).isNotNull();
assertThat(msg).isSameAs(tbMsgList.get(i));
assertThat(resultNodeConnections.get(i))
.isEqualTo(msg.getOriginator().getEntityType().getNormalName());
}
}
private TbMsg getTbMsg(EntityId entityId) {
return TbMsg.newMsg(POST_ATTRIBUTES_REQUEST.name(), entityId, EMPTY_METADATA, EMPTY_DATA);
}
}