hotfix/profile-state-alarm-schedule-dynamic-attributeSource-null

This commit is contained in:
Sergey Matvienko 2025-04-12 11:59:09 +02:00
parent 8966ec8b01
commit 335ec57269
2 changed files with 133 additions and 5 deletions

View File

@ -80,7 +80,7 @@ class ProfileState {
addEntityKeysFromAlarmConditionSpec(alarmRule);
AlarmSchedule schedule = alarmRule.getSchedule();
if (schedule != null) {
addScheduleDynamicValues(schedule);
addScheduleDynamicValues(schedule, entityKeys);
}
}));
if (alarm.getClearRule() != null) {
@ -96,9 +96,9 @@ class ProfileState {
}
}
private void addScheduleDynamicValues(AlarmSchedule schedule) {
void addScheduleDynamicValues(AlarmSchedule schedule, final Set<AlarmConditionFilterKey> entityKeys) {
DynamicValue<String> dynamicValue = schedule.getDynamicValue();
if (dynamicValue != null) {
if (dynamicValue != null && dynamicValue.getSourceAttribute() != null) {
entityKeys.add(
new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE,
dynamicValue.getSourceAttribute())
@ -137,13 +137,14 @@ class ProfileState {
}
private void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<AlarmConditionFilterKey> entityKeys, Set<AlarmConditionFilterKey> ruleKeys) {
void addDynamicValuesRecursively(KeyFilterPredicate predicate, Set<AlarmConditionFilterKey> entityKeys, Set<AlarmConditionFilterKey> ruleKeys) {
switch (predicate.getType()) {
case STRING:
case NUMERIC:
case BOOLEAN:
DynamicValue value = ((SimpleKeyFilterPredicate) predicate).getValue().getDynamicValue();
if (value != null && (value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
if (value != null && value.getSourceAttribute() != null && (
value.getSourceType() == DynamicValueSourceType.CURRENT_TENANT ||
value.getSourceType() == DynamicValueSourceType.CURRENT_CUSTOMER ||
value.getSourceType() == DynamicValueSourceType.CURRENT_DEVICE)) {
AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, value.getSourceAttribute());

View File

@ -0,0 +1,127 @@
/**
* Copyright © 2016-2025 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.profile;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.NullSource;
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
import org.thingsboard.server.common.data.device.profile.SpecificTimeSchedule;
import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
import org.thingsboard.server.common.data.query.FilterPredicateType;
import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate;
import java.util.HashSet;
import java.util.Set;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willCallRealMethod;
import static org.mockito.BDDMockito.willReturn;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class ProfileStateTest {
ProfileState profileState;
Set<AlarmConditionFilterKey> entityKeys = new HashSet<>();
Set<AlarmConditionFilterKey> ruleKeys = new HashSet<>();
@BeforeEach
void setUp() {
profileState = mock(ProfileState.class);
}
@ParameterizedTest()
@EnumSource(DynamicValueSourceType.class)
@NullSource
void addScheduleDynamicValuesSourceAttribute(DynamicValueSourceType sourceType) {
willCallRealMethod().given(profileState).addScheduleDynamicValues(any(), any());
final DynamicValue<String> dynamicValue = new DynamicValue<>(sourceType, "myKey");
SpecificTimeSchedule schedule = new SpecificTimeSchedule();
schedule.setDynamicValue(dynamicValue);
Assertions.assertThat(entityKeys.isEmpty()).isTrue();
Assertions.assertThat(schedule.getDynamicValue().getSourceAttribute()).isNotNull();
profileState.addScheduleDynamicValues(schedule, entityKeys);
Assertions.assertThat(entityKeys).isEqualTo(Set.of(
new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "myKey")));
}
@ParameterizedTest()
@EnumSource(DynamicValueSourceType.class)
@NullSource
void addScheduleDynamicValuesSourceAttributeIsNull(DynamicValueSourceType sourceType) {
willCallRealMethod().given(profileState).addScheduleDynamicValues(any(), any());
DynamicValue<String> dynamicValue = new DynamicValue<>(sourceType, null);
SpecificTimeSchedule schedule = new SpecificTimeSchedule();
schedule.setDynamicValue(dynamicValue);
Assertions.assertThat(entityKeys.isEmpty()).isTrue();
Assertions.assertThat(schedule.getDynamicValue().getSourceAttribute()).isNull();
profileState.addScheduleDynamicValues(schedule, entityKeys);
Assertions.assertThat(entityKeys.isEmpty()).isTrue();
}
@ParameterizedTest()
@EnumSource(value = FilterPredicateType.class, names = {"COMPLEX"})
void addDynamicValuesRecursivelySourceAttributeComplexKeyFilter(FilterPredicateType predicateType) {
willCallRealMethod().given(profileState).addDynamicValuesRecursively(any(), any(), any());
ComplexFilterPredicate predicate = mock(ComplexFilterPredicate.class, RETURNS_DEEP_STUBS);
willReturn(predicateType).given(predicate).getType();
profileState.addDynamicValuesRecursively(predicate, entityKeys, ruleKeys);
Assertions.assertThat(entityKeys.isEmpty()).isTrue();
Assertions.assertThat(ruleKeys.isEmpty()).isTrue();
}
@ParameterizedTest()
@EnumSource(value = FilterPredicateType.class, names = {"STRING", "NUMERIC", "BOOLEAN"})
void addDynamicValuesRecursivelySourceAttributeIsNull(FilterPredicateType predicateType) {
willCallRealMethod().given(profileState).addDynamicValuesRecursively(any(), any(), any());
SimpleKeyFilterPredicate<String> predicate = mock(SimpleKeyFilterPredicate.class, RETURNS_DEEP_STUBS);
willReturn(predicateType).given(predicate).getType();
when(predicate.getValue().getDynamicValue().getSourceType()).thenReturn(DynamicValueSourceType.CURRENT_DEVICE);
when(predicate.getValue().getDynamicValue().getSourceAttribute()).thenReturn(null);
profileState.addDynamicValuesRecursively(predicate, entityKeys, ruleKeys);
Assertions.assertThat(entityKeys.isEmpty()).isTrue();
Assertions.assertThat(ruleKeys.isEmpty()).isTrue();
}
@ParameterizedTest()
@EnumSource(value = FilterPredicateType.class, names = {"STRING", "NUMERIC", "BOOLEAN"})
void addDynamicValuesRecursivelySourceAttributeAdded(FilterPredicateType predicateType) {
willCallRealMethod().given(profileState).addDynamicValuesRecursively(any(), any(), any());
SimpleKeyFilterPredicate<String> predicate = mock(SimpleKeyFilterPredicate.class, RETURNS_DEEP_STUBS);
willReturn(predicateType).given(predicate).getType();
when(predicate.getValue().getDynamicValue().getSourceType()).thenReturn(DynamicValueSourceType.CURRENT_DEVICE);
when(predicate.getValue().getDynamicValue().getSourceAttribute()).thenReturn("myKey");
profileState.addDynamicValuesRecursively(predicate, entityKeys, ruleKeys);
Assertions.assertThat(entityKeys).isEqualTo(Set.of(
new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "myKey")));
Assertions.assertThat(ruleKeys).isEqualTo(Set.of(
new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "myKey")));
}
}