Browse Source

Merge pull request #9652 from ShvaykaD/fix/save-attributes-node

Save attributes node fixes
pull/9771/head
Andrew Shvayka 3 years ago
committed by GitHub
parent
commit
2b4a7e1c58
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json
  2. 8
      application/src/main/data/json/tenant/device_profile/rule_chain_template.json
  3. 8
      application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
  4. 26
      application/src/main/data/upgrade/3.6.1/save_attributes_node_update.sql
  5. 9
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  6. 44
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
  7. 4
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java
  8. 2
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfigurationTest.java
  9. 160
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java

8
application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json

@ -48,12 +48,12 @@
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configurationVersion": 1,
"configurationVersion": 2,
"configuration": {
"scope": "CLIENT_SCOPE",
"notifyDevice": "false",
"sendAttributesUpdatedNotification": "false",
"updateAttributesOnlyOnValueChange": "true"
"notifyDevice": false,
"sendAttributesUpdatedNotification": false,
"updateAttributesOnlyOnValueChange": true
},
"externalId": null
},

8
application/src/main/data/json/tenant/device_profile/rule_chain_template.json

@ -32,12 +32,12 @@
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configurationVersion": 1,
"configurationVersion": 2,
"configuration": {
"scope": "CLIENT_SCOPE",
"notifyDevice": "false",
"sendAttributesUpdatedNotification": "false",
"updateAttributesOnlyOnValueChange": "true"
"notifyDevice": false,
"sendAttributesUpdatedNotification": false,
"updateAttributesOnlyOnValueChange": true
}
},
{

8
application/src/main/data/json/tenant/rule_chains/root_rule_chain.json

@ -31,12 +31,12 @@
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configurationVersion": 1,
"configurationVersion": 2,
"configuration": {
"scope": "CLIENT_SCOPE",
"notifyDevice": "false",
"sendAttributesUpdatedNotification": "false",
"updateAttributesOnlyOnValueChange": "true"
"notifyDevice": false,
"sendAttributesUpdatedNotification": false,
"updateAttributesOnlyOnValueChange": true
}
},
{

26
application/src/main/data/upgrade/3.6.1/save_attributes_node_update.sql

@ -0,0 +1,26 @@
--
-- 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.
--
UPDATE rule_node SET
configuration = (configuration::jsonb || jsonb_build_object(
'notifyDevice',
CASE WHEN configuration::jsonb ->> 'notifyDevice' = 'false' THEN false ELSE true END,
'sendAttributesUpdatedNotification',
CASE WHEN configuration::jsonb ->> 'sendAttributesUpdatedNotification' = 'true' THEN true ELSE false END,
'updateAttributesOnlyOnValueChange',
CASE WHEN configuration::jsonb ->> 'updateAttributesOnlyOnValueChange' = 'false' THEN false ELSE true END)::jsonb)::varchar,
configuration_version = 2
WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version = 1;

9
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -759,7 +759,14 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
updateSchema("3.6.0", 3006000, "3.6.1", 3006001, null);
break;
case "3.6.1":
updateSchema("3.6.1", 3006001, "3.6.2", 3006002, null);
updateSchema("3.6.1", 3006001, "3.6.2", 3006002, connection -> {
try {
Path saveAttributesNodeUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.6.1", "save_attributes_node_update.sql");
loadSql(saveAttributesNodeUpdateFile, connection);
} catch (Exception e) {
log.warn("Failed to execute update script for save attributes rule nodes due to: ", e);
}
});
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);

44
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java

@ -28,13 +28,13 @@ 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.api.util.TbNodeUtils;
import org.thingsboard.server.common.adaptor.JsonConverter;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.util.TbPair;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.adaptor.JsonConverter;
import java.util.ArrayList;
import java.util.List;
@ -43,7 +43,9 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.*;
import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE;
import static org.thingsboard.server.common.data.DataConstants.NOTIFY_DEVICE_METADATA_KEY;
import static org.thingsboard.server.common.data.DataConstants.SCOPE;
import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST;
@Slf4j
@ -51,7 +53,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R
type = ComponentType.ACTION,
name = "save attributes",
configClazz = TbMsgAttributesNodeConfiguration.class,
version = 1,
version = 2,
nodeDescription = "Saves attributes data",
nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type. " +
"If upsert(update/insert) operation is completed successfully rule node will send the incoming message via <b>Success</b> chain, otherwise, <b>Failure</b> chain is used. " +
@ -64,15 +66,15 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R
)
public class TbMsgAttributesNode implements TbNode {
static final String NOTIFY_DEVICE_KEY = "notifyDevice";
static final String SEND_ATTRIBUTES_UPDATED_NOTIFICATION_KEY = "sendAttributesUpdatedNotification";
static final String UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY = "updateAttributesOnlyOnValueChange";
private TbMsgAttributesNodeConfiguration config;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbMsgAttributesNodeConfiguration.class);
if (config.getNotifyDevice() == null) {
config.setNotifyDevice(true);
}
}
@Override
@ -117,7 +119,7 @@ public class TbMsgAttributesNode implements TbNode {
msg.getOriginator(),
scope,
attributes,
checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)),
config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)),
sendAttributesUpdateNotification ?
new AttributesUpdateNodeCallback(ctx, msg, scope, attributes) :
new TelemetryNodeCallback(ctx, msg)
@ -146,8 +148,9 @@ public class TbMsgAttributesNode implements TbNode {
return config.isSendAttributesUpdatedNotification() && !CLIENT_SCOPE.equals(scope);
}
private boolean checkNotifyDevice(String notifyDeviceMdValue) {
return config.getNotifyDevice() || StringUtils.isEmpty(notifyDeviceMdValue) || Boolean.parseBoolean(notifyDeviceMdValue);
private boolean checkNotifyDeviceMdValue(String notifyDeviceMdValue) {
// Check for empty string for backward-compatibility. A while ago node always notified devices.
return StringUtils.isEmpty(notifyDeviceMdValue) || Boolean.parseBoolean(notifyDeviceMdValue);
}
private String getScope(String mdScopeValue) {
@ -166,6 +169,13 @@ public class TbMsgAttributesNode implements TbNode {
hasChanges = true;
((ObjectNode) oldConfiguration).put(UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY, false);
}
case 1:
// update notifyDevice. set true if null or property doesn't exist for backward-compatibility.
hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, NOTIFY_DEVICE_KEY, hasChanges, true);
// update sendAttributesUpdatedNotification.
hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, SEND_ATTRIBUTES_UPDATED_NOTIFICATION_KEY, hasChanges, false);
// update updateAttributesOnlyOnValueChange.
hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY, hasChanges, true);
break;
default:
break;
@ -173,4 +183,20 @@ public class TbMsgAttributesNode implements TbNode {
return new TbPair<>(hasChanges, oldConfiguration);
}
private boolean fixEscapedBooleanConfigParameter(JsonNode oldConfiguration, String boolKey, boolean hasChanges, boolean valueIfNull) {
if (oldConfiguration.hasNonNull(boolKey)) {
var value = oldConfiguration.get(boolKey);
if (value.isTextual()) {
hasChanges = true;
((ObjectNode) oldConfiguration)
.put(boolKey, value.asBoolean(valueIfNull));
}
} else {
hasChanges = true;
((ObjectNode) oldConfiguration)
.put(boolKey, valueIfNull);
}
return hasChanges;
}
}

4
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java

@ -24,7 +24,7 @@ public class TbMsgAttributesNodeConfiguration implements NodeConfiguration<TbMsg
private String scope;
private Boolean notifyDevice;
private boolean notifyDevice;
private boolean sendAttributesUpdatedNotification;
private boolean updateAttributesOnlyOnValueChange;
@ -34,7 +34,7 @@ public class TbMsgAttributesNodeConfiguration implements NodeConfiguration<TbMsg
configuration.setScope(DataConstants.SERVER_SCOPE);
configuration.setNotifyDevice(false);
configuration.setSendAttributesUpdatedNotification(false);
//Since version 1. For an existing rule nodes for version 0. See the TbVersionedNode implementation
// Since version 1. For an existing rule nodes for version 0. See the TbNode implementation
configuration.setUpdateAttributesOnlyOnValueChange(true);
return configuration;
}

2
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfigurationTest.java

@ -22,7 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class TbMsgAttributesNodeConfigurationTest {
@Test
void testDefaultConfig_givenupdateAttributesOnlyOnValueChange_thenTrue_sinceVersion1() {
void testDefaultConfig_givenUpdateAttributesOnlyOnValueChange_thenTrue_sinceVersion1() {
assertThat(new TbMsgAttributesNodeConfiguration().defaultConfiguration().isUpdateAttributesOnlyOnValueChange()).isTrue();
}

160
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java

@ -18,9 +18,20 @@ package org.thingsboard.rule.engine.telemetry;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.ArgumentCaptor;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
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.DataConstants;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
@ -28,27 +39,47 @@ import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.willCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.thingsboard.server.common.data.DataConstants.NOTIFY_DEVICE_METADATA_KEY;
@Slf4j
class TbMsgAttributesNodeTest {
final String updateAttributesOnlyOnValueChangeKey = "updateAttributesOnlyOnValueChange";
private TenantId tenantId;
private DeviceId deviceId;
private TbMsgAttributesNode node;
@BeforeEach
void setUp() {
tenantId = new TenantId(UUID.fromString("6c18691e-4470-4766-9739-aface71d761f"));
deviceId = new DeviceId(UUID.fromString("b66159d7-c77e-45e8-bb41-a8f557f434c1"));
node = spy(TbMsgAttributesNode.class);
}
@Test
void testFilterChangedAttr_whenCurrentAttributesEmpty_thenReturnNewAttributes() {
TbMsgAttributesNode node = spy(TbMsgAttributesNode.class);
List<AttributeKvEntry> newAttributes = new ArrayList<>();
List<AttributeKvEntry> filtered = node.filterChangedAttr(Collections.emptyList(), newAttributes);
@ -57,7 +88,6 @@ class TbMsgAttributesNodeTest {
@Test
void testFilterChangedAttr_whenCurrentAttributesContainsInAnyOrderNewAttributes_thenReturnEmptyList() {
TbMsgAttributesNode node = spy(TbMsgAttributesNode.class);
List<AttributeKvEntry> currentAttributes = List.of(
new BaseAttributeKvEntry(1694000000L, new StringDataEntry("address", "Peremohy ave 1")),
new BaseAttributeKvEntry(1694000000L, new BooleanDataEntry("valid", true)),
@ -78,7 +108,6 @@ class TbMsgAttributesNodeTest {
@Test
void testFilterChangedAttr_whenCurrentAttributesContainsInAnyOrderNewAttributes_thenReturnExpectedList() {
TbMsgAttributesNode node = spy(TbMsgAttributesNode.class);
List<AttributeKvEntry> currentAttributes = List.of(
new BaseAttributeKvEntry(1694000000L, new StringDataEntry("address", "Peremohy ave 1")),
new BaseAttributeKvEntry(1694000000L, new BooleanDataEntry("valid", true)),
@ -103,41 +132,112 @@ class TbMsgAttributesNodeTest {
assertThat(filtered).containsExactlyInAnyOrderElementsOf(expected);
}
@Test
void testUpgrade_fromVersion0() throws TbNodeException {
// Notify device backward-compatibility test arguments
private static Stream<Arguments> givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNotifyDeviceInSaveAndNotifyMethod() {
return Stream.of(
Arguments.of(null, true),
Arguments.of("null", false),
Arguments.of("true", true),
Arguments.of("false", false)
);
}
TbMsgAttributesNode node = mock(TbMsgAttributesNode.class);
willCallRealMethod().given(node).upgrade(anyInt(), any());
// Notify device backward-compatibility test
@ParameterizedTest
@MethodSource
void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNotifyDeviceInSaveAndNotifyMethod(String mdValue, boolean expectedArgumentValue) throws TbNodeException {
var ctxMock = mock(TbContext.class);
var telemetryServiceMock = mock(RuleEngineTelemetryService.class);
ObjectNode defaultConfig = (ObjectNode) JacksonUtil.valueToTree(new TbMsgAttributesNodeConfiguration().defaultConfiguration());
defaultConfig.put("notifyDevice", false);
var tbNodeConfiguration = new TbNodeConfiguration(defaultConfig);
assertThat(defaultConfig.has("notifyDevice")).as("pre condition has notifyDevice").isTrue();
when(ctxMock.getTenantId()).thenReturn(tenantId);
when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock);
willCallRealMethod().given(node).init(any(TbContext.class), any(TbNodeConfiguration.class));
willCallRealMethod().given(node).saveAttr(any(), eq(ctxMock), any(TbMsg.class), anyString(), anyBoolean());
node.init(ctxMock, tbNodeConfiguration);
ObjectNode jsonNode = (ObjectNode) JacksonUtil.valueToTree(new TbMsgAttributesNodeConfiguration().defaultConfiguration());
jsonNode.remove(updateAttributesOnlyOnValueChangeKey);
assertThat(jsonNode.has(updateAttributesOnlyOnValueChangeKey)).as("pre condition has no " + updateAttributesOnlyOnValueChangeKey).isFalse();
TbMsgMetaData md = new TbMsgMetaData();
if (mdValue != null) {
md.putValue(NOTIFY_DEVICE_METADATA_KEY, mdValue);
}
// dummy list with one ts kv to pass the empty list check.
var testTbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, md, TbMsg.EMPTY_STRING);
List<AttributeKvEntry> testAttrList = List.of(new BaseAttributeKvEntry(0L, new StringDataEntry("testKey", "testValue")));
TbPair<Boolean, JsonNode> upgradeResult = node.upgrade(0, jsonNode);
node.saveAttr(testAttrList, ctxMock, testTbMsg, DataConstants.SHARED_SCOPE, false);
ObjectNode resultNode = (ObjectNode) upgradeResult.getSecond();
assertThat(upgradeResult.getFirst()).as("upgrade result has changes").isTrue();
assertThat(resultNode.has(updateAttributesOnlyOnValueChangeKey)).as("upgrade result has key " + updateAttributesOnlyOnValueChangeKey).isTrue();
assertThat(resultNode.get(updateAttributesOnlyOnValueChangeKey).asBoolean()).as("upgrade result value [false] for key " + updateAttributesOnlyOnValueChangeKey).isFalse();
ArgumentCaptor<Boolean> notifyDeviceCaptor = ArgumentCaptor.forClass(Boolean.class);
verify(telemetryServiceMock, times(1)).saveAndNotify(
eq(tenantId), eq(deviceId), eq(DataConstants.SHARED_SCOPE),
eq(testAttrList), notifyDeviceCaptor.capture(), any()
);
boolean notifyDevice = notifyDeviceCaptor.getValue();
assertThat(notifyDevice).isEqualTo(expectedArgumentValue);
}
@Test
void testUpgrade_fromVersion0_alreadyHasupdateAttributesOnlyOnValueChange() throws TbNodeException {
TbMsgAttributesNode node = mock(TbMsgAttributesNode.class);
willCallRealMethod().given(node).upgrade(anyInt(), any());
ObjectNode jsonNode = (ObjectNode) JacksonUtil.valueToTree(new TbMsgAttributesNodeConfiguration().defaultConfiguration());
jsonNode.remove(updateAttributesOnlyOnValueChangeKey);
jsonNode.put(updateAttributesOnlyOnValueChangeKey, true);
assertThat(jsonNode.has(updateAttributesOnlyOnValueChangeKey)).as("pre condition has no " + updateAttributesOnlyOnValueChangeKey).isTrue();
assertThat(jsonNode.get(updateAttributesOnlyOnValueChangeKey).asBoolean()).as("pre condition has [true] for key " + updateAttributesOnlyOnValueChangeKey).isTrue();
// Rule nodes upgrade
private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() {
return Stream.of(
// default config for version 0
Arguments.of(0,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":\"false\"}",
true,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":false}"),
// default config for version 1 with upgrade from version 0
Arguments.of(0,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}",
false,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"),
// all flags are booleans
Arguments.of(1,
"{\"scope\":\"SHARED_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}",
false,
"{\"scope\":\"SHARED_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"),
// no boolean flags set
Arguments.of(1,
"{\"scope\":\"CLIENT_SCOPE\"}",
true,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"),
// all flags are boolean strings
Arguments.of(1,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":\"false\",\"updateAttributesOnlyOnValueChange\":\"true\"}",
true,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"),
// at least one flag is boolean string
Arguments.of(1,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}",
true,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"),
// notify device flag is null
Arguments.of(1,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"null\",\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}",
true,
"{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}")
);
}
@ParameterizedTest
@MethodSource
void givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig(int givenVersion, String givenConfigStr, boolean hasChanges, String expectedConfigStr) throws TbNodeException {
// GIVEN
willCallRealMethod().given(node).upgrade(anyInt(), any());
JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr);
JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr);
TbPair<Boolean, JsonNode> upgradeResult = node.upgrade(0, jsonNode);
// WHEN
TbPair<Boolean, JsonNode> upgradeResult = node.upgrade(givenVersion, givenConfig);
ObjectNode resultNode = (ObjectNode) upgradeResult.getSecond();
assertThat(upgradeResult.getFirst()).as("upgrade result has changes").isFalse();
assertThat(resultNode.has(updateAttributesOnlyOnValueChangeKey)).as("upgrade result has key " + updateAttributesOnlyOnValueChangeKey).isTrue();
assertThat(resultNode.get(updateAttributesOnlyOnValueChangeKey).asBoolean()).as("upgrade result value [true] for key " + updateAttributesOnlyOnValueChangeKey).isTrue();
// THEN
assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges);
ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond();
assertThat(upgradedConfig).isEqualTo(expectedConfig);
}
}

Loading…
Cancel
Save