Browse Source

Merge branch 'master' into fix/3969-LwM2M-resource-title-read-only

pull/11198/head
Max Petrov 2 years ago
committed by GitHub
parent
commit
b1cc582691
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      application/src/main/data/json/system/widget_types/update_server_string_attribute.json
  2. 2
      application/src/main/data/json/system/widget_types/update_shared_string_attribute.json
  3. 2
      application/src/main/data/json/system/widget_types/update_string_timeseries.json
  4. 1
      application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java
  5. 7
      application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java
  6. 2
      common/cache/src/main/java/org/thingsboard/server/cache/CacheSpecsMap.java
  7. 39
      common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java
  8. 34
      common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java
  9. 3
      common/cache/src/test/java/org/thingsboard/server/cache/CacheSpecsMapTest.java
  10. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java
  11. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java
  12. 15
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  13. 82
      dao/src/test/java/org/thingsboard/server/dao/cache/RedisTbTransactionalCacheTest.java
  14. 87
      dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
  15. 24
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  16. 2
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java
  17. 4
      ui-ngx/src/app/core/services/utils.service.ts
  18. 2
      ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html
  19. 24
      ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts
  20. 2
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts
  21. 14
      ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
  22. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html
  23. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.html
  24. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html
  25. 2
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts
  26. 1
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  27. 2
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  28. 49
      ui-ngx/src/app/shared/components/entity/entity-list.component.ts

2
application/src/main/data/json/system/widget_types/update_server_string_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_shared_string_attribute.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_types/update_string_timeseries.json

File diff suppressed because one or more lines are too long

1
application/src/test/java/org/thingsboard/server/edge/NotificationEdgeTest.java

@ -18,7 +18,6 @@ package org.thingsboard.server.edge;
import com.google.protobuf.AbstractMessage; import com.google.protobuf.AbstractMessage;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;

7
application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java

@ -59,12 +59,7 @@ public class OAuth2EdgeTest extends AbstractEdgeTest {
oAuth2Info.setEnabled(false); oAuth2Info.setEnabled(false);
oAuth2Info.setEdgeEnabled(false); oAuth2Info.setEdgeEnabled(false);
doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertFalse(edgeImitator.waitForMessages(5));
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg);
oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage;
result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true);
Assert.assertEquals(oAuth2Info, result);
edgeImitator.ignoreType(OAuth2UpdateMsg.class); edgeImitator.ignoreType(OAuth2UpdateMsg.class);
loginTenantAdmin(); loginTenantAdmin();

2
common/cache/src/main/java/org/thingsboard/server/cache/CacheSpecsMap.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.cache; package org.thingsboard.server.cache;
import jakarta.annotation.PostConstruct;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -22,7 +23,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.CacheConstants;
import jakarta.annotation.PostConstruct;
import java.util.Map; import java.util.Map;
@Configuration @Configuration

39
common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java

@ -29,7 +29,6 @@ import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.thingsboard.server.common.data.FstStatsService; import org.thingsboard.server.common.data.FstStatsService;
import redis.clients.jedis.Connection;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.JedisClusterCRC16;
@ -40,6 +39,8 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
@Slf4j @Slf4j
public abstract class RedisTbTransactionalCache<K extends Serializable, V extends Serializable> implements TbTransactionalCache<K, V> { public abstract class RedisTbTransactionalCache<K extends Serializable, V extends Serializable> implements TbTransactionalCache<K, V> {
@ -57,6 +58,7 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
private final TbRedisSerializer<K, V> valueSerializer; private final TbRedisSerializer<K, V> valueSerializer;
private final Expiration evictExpiration; private final Expiration evictExpiration;
private final Expiration cacheTtl; private final Expiration cacheTtl;
private final boolean cacheEnabled;
public RedisTbTransactionalCache(String cacheName, public RedisTbTransactionalCache(String cacheName,
CacheSpecsMap cacheSpecsMap, CacheSpecsMap cacheSpecsMap,
@ -73,10 +75,19 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
.map(CacheSpecs::getTimeToLiveInMinutes) .map(CacheSpecs::getTimeToLiveInMinutes)
.map(t -> Expiration.from(t, TimeUnit.MINUTES)) .map(t -> Expiration.from(t, TimeUnit.MINUTES))
.orElseGet(Expiration::persistent); .orElseGet(Expiration::persistent);
this.cacheEnabled = Optional.ofNullable(cacheSpecsMap)
.map(CacheSpecsMap::getSpecs)
.map(x -> x.get(cacheName))
.map(CacheSpecs::getMaxSize)
.map(size -> size > 0)
.orElse(false);
} }
@Override @Override
public TbCacheValueWrapper<V> get(K key) { public TbCacheValueWrapper<V> get(K key) {
if (!cacheEnabled) {
return null;
}
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
byte[] rawKey = getRawKey(key); byte[] rawKey = getRawKey(key);
byte[] rawValue = connection.get(rawKey); byte[] rawValue = connection.get(rawKey);
@ -98,6 +109,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override @Override
public void put(K key, V value) { public void put(K key, V value) {
if (!cacheEnabled) {
return;
}
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
put(connection, key, value, RedisStringCommands.SetOption.UPSERT); put(connection, key, value, RedisStringCommands.SetOption.UPSERT);
} }
@ -105,6 +119,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override @Override
public void putIfAbsent(K key, V value) { public void putIfAbsent(K key, V value) {
if (!cacheEnabled) {
return;
}
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
put(connection, key, value, RedisStringCommands.SetOption.SET_IF_ABSENT); put(connection, key, value, RedisStringCommands.SetOption.SET_IF_ABSENT);
} }
@ -112,6 +129,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override @Override
public void evict(K key) { public void evict(K key) {
if (!cacheEnabled) {
return;
}
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
connection.del(getRawKey(key)); connection.del(getRawKey(key));
} }
@ -119,6 +139,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override @Override
public void evict(Collection<K> keys) { public void evict(Collection<K> keys) {
if (!cacheEnabled) {
return;
}
//Redis expects at least 1 key to delete. Otherwise - ERR wrong number of arguments for 'del' command //Redis expects at least 1 key to delete. Otherwise - ERR wrong number of arguments for 'del' command
if (keys.isEmpty()) { if (keys.isEmpty()) {
return; return;
@ -130,6 +153,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override @Override
public void evictOrPut(K key, V value) { public void evictOrPut(K key, V value) {
if (!cacheEnabled) {
return;
}
try (var connection = connectionFactory.getConnection()) { try (var connection = connectionFactory.getConnection()) {
var rawKey = getRawKey(key); var rawKey = getRawKey(key);
var records = connection.del(rawKey); var records = connection.del(rawKey);
@ -153,6 +179,14 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
return new RedisTbCacheTransaction<>(this, connection); return new RedisTbCacheTransaction<>(this, connection);
} }
@Override
public <R> R getAndPutInTransaction(K key, Supplier<R> dbCall, Function<V, R> cacheValueToResult, Function<R, V> dbValueToCacheValue, boolean cacheNullValue) {
if (!cacheEnabled) {
return dbCall.get();
}
return TbTransactionalCache.super.getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue);
}
private RedisConnection getConnection(byte[] rawKey) { private RedisConnection getConnection(byte[] rawKey) {
if (!connectionFactory.isRedisClusterAware()) { if (!connectionFactory.isRedisClusterAware()) {
return connectionFactory.getConnection(); return connectionFactory.getConnection();
@ -214,6 +248,9 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
} }
public void put(RedisConnection connection, K key, V value, RedisStringCommands.SetOption setOption) { public void put(RedisConnection connection, K key, V value, RedisStringCommands.SetOption setOption) {
if (!cacheEnabled) {
return;
}
byte[] rawKey = getRawKey(key); byte[] rawKey = getRawKey(key);
byte[] rawValue = getRawValue(value); byte[] rawValue = getRawValue(value);
connection.set(rawKey, rawValue, cacheTtl, setOption); connection.set(rawKey, rawValue, cacheTtl, setOption);

34
common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java

@ -60,15 +60,20 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
} }
default V getAndPutInTransaction(K key, Supplier<V> dbCall, boolean cacheNullValue) { default V getAndPutInTransaction(K key, Supplier<V> dbCall, boolean cacheNullValue) {
return getAndPutInTransaction(key, dbCall, Function.identity(), Function.identity(), cacheNullValue);
}
default <R> R getAndPutInTransaction(K key, Supplier<R> dbCall, Function<V, R> cacheValueToResult, Function<R, V> dbValueToCacheValue, boolean cacheNullValue) {
TbCacheValueWrapper<V> cacheValueWrapper = get(key); TbCacheValueWrapper<V> cacheValueWrapper = get(key);
if (cacheValueWrapper != null) { if (cacheValueWrapper != null) {
return cacheValueWrapper.get(); V cacheValue = cacheValueWrapper.get();
return cacheValue != null ? cacheValueToResult.apply(cacheValue) : null;
} }
var cacheTransaction = newTransactionForKey(key); var cacheTransaction = newTransactionForKey(key);
try { try {
V dbValue = dbCall.get(); R dbValue = dbCall.get();
if (dbValue != null || cacheNullValue) { if (dbValue != null || cacheNullValue) {
cacheTransaction.putIfAbsent(key, dbValue); cacheTransaction.putIfAbsent(key, dbValueToCacheValue.apply(dbValue));
cacheTransaction.commit(); cacheTransaction.commit();
return dbValue; return dbValue;
} else { } else {
@ -94,27 +99,4 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
} }
} }
default <R> R getAndPutInTransaction(K key, Supplier<R> dbCall, Function<V, R> cacheValueToResult, Function<R, V> dbValueToCacheValue, boolean cacheNullValue) {
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
if (cacheValueWrapper != null) {
var cacheValue = cacheValueWrapper.get();
return cacheValue == null ? null : cacheValueToResult.apply(cacheValue);
}
var cacheTransaction = newTransactionForKey(key);
try {
R dbValue = dbCall.get();
if (dbValue != null || cacheNullValue) {
cacheTransaction.putIfAbsent(key, dbValueToCacheValue.apply(dbValue));
cacheTransaction.commit();
return dbValue;
} else {
cacheTransaction.rollback();
return null;
}
} catch (Throwable e) {
cacheTransaction.rollback();
throw e;
}
}
} }

3
common/cache/src/test/java/org/thingsboard/server/cache/CacheSpecsMapTest.java

@ -62,4 +62,5 @@ public class CacheSpecsMapTest {
public void givenCacheConfig_whenCacheManagerReady_thenVerifyNonExistedCaches() { public void givenCacheConfig_whenCacheManagerReady_thenVerifyNonExistedCaches() {
assertThat(cacheManager.getCache("rainbows_and_unicorns")).isNull(); assertThat(cacheManager.getCache("rainbows_and_unicorns")).isNull();
} }
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.ToString;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -53,9 +54,12 @@ public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationReques
private long startTs; private long startTs;
@Schema(description = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522") @Schema(description = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522")
private long endTs; private long endTs;
@ToString.Exclude
@NoXss @NoXss
@Schema(description = "JSON object with alarm details") @Schema(description = "JSON object with alarm details")
private JsonNode details; private JsonNode details;
@Valid @Valid
@Schema(description = "JSON object with propagation details") @Schema(description = "JSON object with propagation details")
private AlarmPropagationInfo propagation; private AlarmPropagationInfo propagation;

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.ToString;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
@ -47,9 +48,12 @@ public class AlarmUpdateRequest implements AlarmModificationRequest {
private long startTs; private long startTs;
@Schema(description = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522") @Schema(description = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522")
private long endTs; private long endTs;
@ToString.Exclude
@NoXss @NoXss
@Schema(description = "JSON object with alarm details") @Schema(description = "JSON object with alarm details")
private JsonNode details; private JsonNode details;
@Valid @Valid
@Schema(description = "JSON object with propagation details") @Schema(description = "JSON object with propagation details")
private AlarmPropagationInfo propagation; private AlarmPropagationInfo propagation;

15
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -342,9 +342,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override @Override
public AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) { public AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) {
UUID tenantUUID = request.getTenantId().getId();
log.debug("[{}] createOrUpdateActiveAlarm [{}] {}", tenantUUID, alarmCreationEnabled, request);
AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation()); AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation());
return toAlarmApiResult(alarmRepository.createOrUpdateActiveAlarm( return toAlarmApiResult(alarmRepository.createOrUpdateActiveAlarm(
request.getTenantId().getId(), tenantUUID,
request.getCustomerId() != null ? request.getCustomerId().getId() : CustomerId.NULL_UUID, request.getCustomerId() != null ? request.getCustomerId().getId() : CustomerId.NULL_UUID,
request.getEdgeAlarmId() != null ? request.getEdgeAlarmId().getId() : UUID.randomUUID(), request.getEdgeAlarmId() != null ? request.getEdgeAlarmId().getId() : UUID.randomUUID(),
System.currentTimeMillis(), System.currentTimeMillis(),
@ -364,10 +367,14 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override @Override
public AlarmApiCallResult updateAlarm(AlarmUpdateRequest request) { public AlarmApiCallResult updateAlarm(AlarmUpdateRequest request) {
UUID tenantUUID = request.getTenantId().getId();
UUID alarmUUID = request.getAlarmId().getId();
log.debug("[{}][{}] updateAlarm {}", tenantUUID, alarmUUID, request);
AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation()); AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation());
return toAlarmApiResult(alarmRepository.updateAlarm( return toAlarmApiResult(alarmRepository.updateAlarm(
request.getTenantId().getId(), tenantUUID,
request.getAlarmId().getId(), alarmUUID,
request.getSeverity().name(), request.getSeverity().name(),
request.getStartTs(), request.getEndTs(), request.getStartTs(), request.getEndTs(),
getDetailsAsString(request.getDetails()), getDetailsAsString(request.getDetails()),
@ -380,11 +387,13 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override @Override
public AlarmApiCallResult acknowledgeAlarm(TenantId tenantId, AlarmId id, long ackTs) { public AlarmApiCallResult acknowledgeAlarm(TenantId tenantId, AlarmId id, long ackTs) {
log.debug("[{}][{}] acknowledgeAlarm [{}]", tenantId, id, ackTs);
return toAlarmApiResult(alarmRepository.acknowledgeAlarm(tenantId.getId(), id.getId(), ackTs)); return toAlarmApiResult(alarmRepository.acknowledgeAlarm(tenantId.getId(), id.getId(), ackTs));
} }
@Override @Override
public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId id, long clearTs, JsonNode details) { public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId id, long clearTs, JsonNode details) {
log.debug("[{}][{}] clearAlarm [{}]", tenantId, id, clearTs);
return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, details != null ? getDetailsAsString(details) : null)); return toAlarmApiResult(alarmRepository.clearAlarm(tenantId.getId(), id.getId(), clearTs, details != null ? getDetailsAsString(details) : null));
} }

82
dao/src/test/java/org/thingsboard/server/dao/cache/RedisTbTransactionalCacheTest.java

@ -0,0 +1,82 @@
/**
* Copyright © 2016-2024 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.dao.cache;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisSslCredentials;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.relation.RelationCacheKey;
import org.thingsboard.server.dao.relation.RelationRedisCache;
import java.util.List;
import java.util.UUID;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {RelationRedisCache.class, CacheSpecsMap.class, TBRedisCacheConfiguration.class})
@TestPropertySource(properties = {
"cache.type=redis",
"cache.specs.relations.timeToLiveInMinutes=1440",
"cache.specs.relations.maxSize=0",
})
@Slf4j
public class RedisTbTransactionalCacheTest {
@MockBean
private RelationRedisCache relationRedisCache;
@MockBean
private RedisConnectionFactory connectionFactory;
@MockBean
private RedisConnection redisConnection;
@MockBean
private RedisSslCredentials redisSslCredentials;
@Test
public void testNoOpWhenCacheDisabled() {
when(connectionFactory.getConnection()).thenReturn(redisConnection);
relationRedisCache.put(createRelationCacheKey(), null);
relationRedisCache.putIfAbsent(createRelationCacheKey(), null);
relationRedisCache.evict(createRelationCacheKey());
relationRedisCache.evict(List.of(createRelationCacheKey()));
relationRedisCache.getAndPutInTransaction(createRelationCacheKey(), null, false);
relationRedisCache.getAndPutInTransaction(createRelationCacheKey(), null, null, null, false);
relationRedisCache.getOrFetchFromDB(createRelationCacheKey(), null, false, false);
verify(connectionFactory, never()).getConnection();
verifyNoInteractions(redisConnection);
}
private RelationCacheKey createRelationCacheKey() {
return new RelationCacheKey(new DeviceId(UUID.randomUUID()), new DeviceId(UUID.randomUUID()), null, RelationTypeGroup.COMMON);
}
}

87
dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java

@ -58,7 +58,6 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -245,8 +244,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
// Check child relation // Check child relation
PageData<AlarmInfo> alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() PageData<AlarmInfo> alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -257,8 +256,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
// Check parent relation // Check parent relation
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(parentId) .affectedEntityId(parentId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -272,8 +271,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
// Check child relation // Check child relation
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -284,8 +283,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
// Check parent relation // Check parent relation
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(parentId) .affectedEntityId(parentId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -298,8 +297,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.ACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.ACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -310,8 +309,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
// Check not existing relation // Check not existing relation
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -323,8 +322,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL)) .severityList(List.of(AlarmSeverity.CRITICAL))
.statusList(Arrays.asList(AlarmSearchStatus.CLEARED, AlarmSearchStatus.ACK)).pageLink( .statusList(List.of(AlarmSearchStatus.CLEARED, AlarmSearchStatus.ACK)).pageLink(
new TimePageLink(1, 0, "", new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -334,7 +333,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void testFindAssignedAlarm() throws ExecutionException, InterruptedException { public void testFindAssignedAlarm() {
AssetId parentId = new AssetId(Uuids.timeBased()); AssetId parentId = new AssetId(Uuids.timeBased());
AssetId childId = new AssetId(Uuids.timeBased()); AssetId childId = new AssetId(Uuids.timeBased());
@ -368,7 +367,6 @@ public class AlarmServiceTest extends AbstractServiceTest {
PageData<AlarmInfo> alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() PageData<AlarmInfo> alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.assigneeId(tenantUser.getId()) .assigneeId(tenantUser.getId())
.fetchOriginator(true)
.pageLink(new TimePageLink(1, 0, "", .pageLink(new TimePageLink(1, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
).build()); ).build());
@ -405,7 +403,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void testFindCustomerAlarm() throws ExecutionException, InterruptedException { public void testFindCustomerAlarm() {
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("TestCustomer"); customer.setTitle("TestCustomer");
customer.setTenantId(tenantId); customer.setTenantId(tenantId);
@ -451,10 +449,10 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Arrays.asList(tenantDevice.getId(), customerDevice.getId())); PageData<AlarmData> tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), List.of(tenantDevice.getId(), customerDevice.getId()));
Assert.assertEquals(2, tenantAlarms.getData().size()); Assert.assertEquals(2, tenantAlarms.getData().size());
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerDevice.getId())); PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerDevice.getId()));
@ -473,7 +471,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void testFindPropagatedCustomerAssetAlarm() throws ExecutionException, InterruptedException { public void testFindPropagatedCustomerAssetAlarm() {
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("TestCustomer"); customer.setTitle("TestCustomer");
customer.setTenantId(tenantId); customer.setTenantId(tenantId);
@ -525,8 +523,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
//TEST that propagated alarms are visible on the asset level. //TEST that propagated alarms are visible on the asset level.
PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerAsset.getId())); PageData<AlarmData> customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerAsset.getId()));
@ -576,7 +574,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Collections.singletonList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(Collections.singletonList(AlarmSearchStatus.ACTIVE));
//TEST that propagated alarms are visible on the asset level. //TEST that propagated alarms are visible on the asset level.
@ -599,7 +597,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void testFindHighestAlarmSeverity() throws ExecutionException, InterruptedException { public void testFindHighestAlarmSeverity() {
Customer customer = new Customer(); Customer customer = new Customer();
customer.setTitle("TestCustomer"); customer.setTitle("TestCustomer");
customer.setTenantId(tenantId); customer.setTenantId(tenantId);
@ -679,8 +677,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(false); pageLink.setSearchPropagatedAlarms(false);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
@ -695,8 +693,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(false); pageLink.setSearchPropagatedAlarms(false);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
@ -722,8 +720,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
@ -738,8 +736,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId)); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId));
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
@ -748,7 +746,6 @@ public class AlarmServiceTest extends AbstractServiceTest {
PageData<AlarmInfo> alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() PageData<AlarmInfo> alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.fetchOriginator(true)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(10, 0, "", new TimePageLink(10, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
@ -759,7 +756,6 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(parentId) .affectedEntityId(parentId)
.fetchOriginator(true)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(10, 0, "", new TimePageLink(10, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
@ -770,7 +766,6 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(parentId2) .affectedEntityId(parentId2)
.fetchOriginator(true)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(10, 0, "", new TimePageLink(10, 0, "",
new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis())
@ -786,8 +781,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId)); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId));
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
@ -803,8 +798,8 @@ public class AlarmServiceTest extends AbstractServiceTest {
pageLink.setStartTs(0L); pageLink.setStartTs(0L);
pageLink.setEndTs(System.currentTimeMillis()); pageLink.setEndTs(System.currentTimeMillis());
pageLink.setSearchPropagatedAlarms(true); pageLink.setSearchPropagatedAlarms(true);
pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); pageLink.setSeverityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); pageLink.setStatusList(List.of(AlarmSearchStatus.ACTIVE));
alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId));
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
@ -813,7 +808,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
} }
@Test @Test
public void testCountAlarmsUsingAlarmDataQuery() throws ExecutionException, InterruptedException { public void testCountAlarmsUsingAlarmDataQuery() {
AssetId childId = new AssetId(Uuids.timeBased()); AssetId childId = new AssetId(Uuids.timeBased());
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
@ -829,7 +824,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
.startTs(0L) .startTs(0L)
.endTs(System.currentTimeMillis()) .endTs(System.currentTimeMillis())
.searchPropagatedAlarms(false) .searchPropagatedAlarms(false)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)) .severityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
.statusList(List.of(AlarmSearchStatus.ACTIVE)) .statusList(List.of(AlarmSearchStatus.ACTIVE))
.build(); .build();
@ -841,7 +836,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
.startTs(0L) .startTs(0L)
.endTs(System.currentTimeMillis()) .endTs(System.currentTimeMillis())
.searchPropagatedAlarms(true) .searchPropagatedAlarms(true)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)) .severityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
.statusList(List.of(AlarmSearchStatus.ACTIVE)) .statusList(List.of(AlarmSearchStatus.ACTIVE))
.build(); .build();
@ -866,7 +861,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
.startTs(0L) .startTs(0L)
.endTs(System.currentTimeMillis()) .endTs(System.currentTimeMillis())
.searchPropagatedAlarms(true) .searchPropagatedAlarms(true)
.severityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)) .severityList(List.of(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING))
.statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.CLEARED)) .statusList(List.of(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.CLEARED))
.build(); .build();
@ -939,6 +934,6 @@ public class AlarmServiceTest extends AbstractServiceTest {
).build()); ).build());
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size()); Assert.assertEquals(0, alarms.getData().size());
} }
} }

24
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java

@ -27,10 +27,10 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.client.reactive.ReactorClientHttpConnector; import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec; import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
@ -132,6 +132,7 @@ public class TbHttpClient {
this.webClient = WebClient.builder() this.webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient)) .clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader(HttpHeaders.CONNECTION, "close") //In previous realization this header was present! (Added for hotfix "Connection reset")
.build(); .build();
} catch (SSLException e) { } catch (SSLException e) {
throw new TbNodeException(e); throw new TbNodeException(e);
@ -170,7 +171,7 @@ public class TbHttpClient {
BiConsumer<TbMsg, Throwable> onFailure) { BiConsumer<TbMsg, Throwable> onFailure) {
try { try {
if (semaphore != null && !semaphore.tryAcquire(config.getReadTimeoutMs(), TimeUnit.MILLISECONDS)) { if (semaphore != null && !semaphore.tryAcquire(config.getReadTimeoutMs(), TimeUnit.MILLISECONDS)) {
ctx.tellFailure(msg, new RuntimeException("Timeout during waiting for reply!")); onFailure.accept(msg, new RuntimeException("Timeout during waiting for reply!"));
return; return;
} }
@ -183,10 +184,10 @@ public class TbHttpClient {
.uri(uri) .uri(uri)
.headers(headers -> prepareHeaders(headers, msg)); .headers(headers -> prepareHeaders(headers, msg));
if (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method) || if ((HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method) ||
HttpMethod.PATCH.equals(method) || HttpMethod.DELETE.equals(method) || HttpMethod.PATCH.equals(method) || HttpMethod.DELETE.equals(method)) &&
!config.isIgnoreRequestBody()) { !config.isIgnoreRequestBody()) {
request.body(BodyInserters.fromValue(getData(msg, config.isIgnoreRequestBody(), config.isParseToPlainText()))); request.body(BodyInserters.fromValue(getData(msg, config.isParseToPlainText())));
} }
request request
@ -236,11 +237,9 @@ public class TbHttpClient {
return uri; return uri;
} }
private String getData(TbMsg tbMsg, boolean ignoreBody, boolean parseToPlainText) { private Object getData(TbMsg tbMsg, boolean parseToPlainText) {
if (!ignoreBody && parseToPlainText) { String data = tbMsg.getData();
return JacksonUtil.toPlainText(tbMsg.getData()); return parseToPlainText ? JacksonUtil.toPlainText(data) : JacksonUtil.toJsonNode(data);
}
return tbMsg.getData();
} }
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) { private TbMsg processResponse(TbContext ctx, TbMsg origMsg, ResponseEntity<String> response) {
@ -283,10 +282,9 @@ public class TbHttpClient {
private TbMsg processException(TbMsg origMsg, Throwable e) { private TbMsg processException(TbMsg origMsg, Throwable e) {
TbMsgMetaData metaData = origMsg.getMetaData(); TbMsgMetaData metaData = origMsg.getMetaData();
metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage());
if (e instanceof RestClientResponseException) { if (e instanceof WebClientResponseException restClientResponseException) {
RestClientResponseException restClientResponseException = (RestClientResponseException) e;
metaData.putValue(STATUS, restClientResponseException.getStatusText()); metaData.putValue(STATUS, restClientResponseException.getStatusText());
metaData.putValue(STATUS_CODE, restClientResponseException.getRawStatusCode() + ""); metaData.putValue(STATUS_CODE, restClientResponseException.getStatusCode().value() + "");
metaData.putValue(ERROR_BODY, restClientResponseException.getResponseBodyAsString()); metaData.putValue(ERROR_BODY, restClientResponseException.getResponseBodyAsString());
} }
return TbMsg.transformMsgMetadata(origMsg, metaData); return TbMsg.transformMsgMetadata(origMsg, metaData);

2
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java

@ -168,7 +168,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest {
try { try {
assertEquals(path, request.getRequestLine().getUri(), "Request path matches"); assertEquals(path, request.getRequestLine().getUri(), "Request path matches");
assertTrue(request.containsHeader("Content-Type"), "Content-Type included"); assertTrue(request.containsHeader("Content-Type"), "Content-Type included");
assertEquals("text/plain;charset=UTF-8", assertEquals("application/json",
request.getFirstHeader("Content-Type").getValue(), "Content-Type value"); request.getFirstHeader("Content-Type").getValue(), "Content-Type value");
assertTrue(request.containsHeader("Content-Length"), "Content-Length included"); assertTrue(request.containsHeader("Content-Length"), "Content-Length included");
assertEquals("2", assertEquals("2",

4
ui-ngx/src/app/core/services/utils.service.ts

@ -456,6 +456,10 @@ export class UtilsService {
return isDefined(value); return isDefined(value);
} }
public isDefinedAndNotNull(value: any): boolean {
return isDefinedAndNotNull(value);
}
public defaultValue(value: any, defaultValue: any): any { public defaultValue(value: any, defaultValue: any): any {
if (isDefinedAndNotNull(value)) { if (isDefinedAndNotNull(value)) {
return value; return value;

2
ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html

@ -160,7 +160,7 @@
</div> </div>
<tb-chart-fill-settings <tb-chart-fill-settings
formControlName="barBackgroundSettings" formControlName="barBackgroundSettings"
title="widgets.chart.background" titleText="widgets.chart.background"
fillNoneTitle="widgets.chart.fill-type-solid"> fillNoneTitle="widgets.chart.fill-type-solid">
</tb-chart-fill-settings> </tb-chart-fill-settings>
<tb-time-series-no-aggregation-bar-width-settings <tb-time-series-no-aggregation-bar-width-settings

24
ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts

@ -203,23 +203,29 @@ export class UnreadNotificationWidgetComponent implements OnInit, OnDestroy {
} }
markAsRead(id: string) { markAsRead(id: string) {
const cmd = NotificationSubscriber.createMarkAsReadCommand(this.notificationWsService, [id]); if (!this.ctx.isEdit && !this.ctx.isPreview) {
cmd.subscribe(); const cmd = NotificationSubscriber.createMarkAsReadCommand(this.notificationWsService, [id]);
cmd.subscribe();
}
} }
markAsAllRead($event: Event) { markAsAllRead($event: Event) {
if ($event) { if (!this.ctx.isEdit && !this.ctx.isPreview) {
$event.stopPropagation(); if ($event) {
$event.stopPropagation();
}
const cmd = NotificationSubscriber.createMarkAllAsReadCommand(this.notificationWsService);
cmd.subscribe();
} }
const cmd = NotificationSubscriber.createMarkAllAsReadCommand(this.notificationWsService);
cmd.subscribe();
} }
viewAll($event: Event) { viewAll($event: Event) {
if ($event) { if (!this.ctx.isEdit && !this.ctx.isPreview) {
$event.stopPropagation(); if ($event) {
$event.stopPropagation();
}
this.router.navigateByUrl(this.router.parseUrl('/notification/inbox')).then(() => {});
} }
this.router.navigateByUrl(this.router.parseUrl('/notification/inbox')).then(() => {});
} }
trackById(index: number, item: NotificationRequest): string { trackById(index: number, item: NotificationRequest): string {

2
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts

@ -169,6 +169,6 @@ export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterV
} }
public toggleLegendKey(legendKey: LegendKey) { public toggleLegendKey(legendKey: LegendKey) {
this.timeSeriesChart.toggleKey(legendKey.dataKey); this.timeSeriesChart.toggleKey(legendKey.dataKey, legendKey.dataIndex);
} }
} }

14
ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts

@ -56,7 +56,14 @@ import {
measureAxisNameSize measureAxisNameSize
} from '@home/components/widget/lib/chart/echarts-widget.models'; } from '@home/components/widget/lib/chart/echarts-widget.models';
import { DateFormatProcessor, ValueSourceType } from '@shared/models/widget-settings.models'; import { DateFormatProcessor, ValueSourceType } from '@shared/models/widget-settings.models';
import { formattedDataFormDatasourceData, formatValue, isDefinedAndNotNull, isEqual, mergeDeep } from '@core/utils'; import {
formattedDataFormDatasourceData,
formatValue,
isDefined,
isDefinedAndNotNull,
isEqual,
mergeDeep
} from '@core/utils';
import { DataKey, Datasource, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models'; import { DataKey, Datasource, DatasourceType, FormattedData, widgetType } from '@shared/models/widget.models';
import * as echarts from 'echarts/core'; import * as echarts from 'echarts/core';
import { CallbackDataParams, PiecewiseVisualMapOption } from 'echarts/types/dist/shared'; import { CallbackDataParams, PiecewiseVisualMapOption } from 'echarts/types/dist/shared';
@ -300,7 +307,7 @@ export class TbTimeSeriesChart {
} }
} }
public toggleKey(dataKey: DataKey): void { public toggleKey(dataKey: DataKey, dataIndex?: number): void {
const enable = dataKey.hidden; const enable = dataKey.hidden;
const dataItem = this.dataItems.find(d => d.dataKey === dataKey); const dataItem = this.dataItems.find(d => d.dataKey === dataKey);
if (dataItem) { if (dataItem) {
@ -320,6 +327,9 @@ export class TbTimeSeriesChart {
this.timeSeriesChart.setOption(this.timeSeriesChartOptions, this.stackMode ? {notMerge: true} : {replaceMerge: mergeList}); this.timeSeriesChart.setOption(this.timeSeriesChartOptions, this.stackMode ? {notMerge: true} : {replaceMerge: mergeList});
this.updateAxes(); this.updateAxes();
dataKey.hidden = !enable; dataKey.hidden = !enable;
if (isDefined(dataIndex)) {
this.ctx.defaultSubscription.updateDataVisibility(dataIndex);
}
if (enable) { if (enable) {
this.timeSeriesChart.dispatchAction({ this.timeSeriesChart.dispatchAction({
type: 'highlight', type: 'highlight',

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html

@ -79,7 +79,7 @@
</div> </div>
<tb-chart-fill-settings <tb-chart-fill-settings
formControlName="barBackgroundSettings" formControlName="barBackgroundSettings"
title="widgets.chart.background" titleText="widgets.chart.background"
fillNoneTitle="widgets.chart.fill-type-solid"> fillNoneTitle="widgets.chart.fill-type-solid">
</tb-chart-fill-settings> </tb-chart-fill-settings>
<tb-time-series-no-aggregation-bar-width-settings <tb-time-series-no-aggregation-bar-width-settings

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-bar-settings.component.html

@ -77,7 +77,7 @@
</div> </div>
<tb-chart-fill-settings <tb-chart-fill-settings
formControlName="backgroundSettings" formControlName="backgroundSettings"
title="widgets.chart.background" titleText="widgets.chart.background"
fillNoneTitle="widgets.chart.fill-type-solid"> fillNoneTitle="widgets.chart.fill-type-solid">
</tb-chart-fill-settings> </tb-chart-fill-settings>
</ng-container> </ng-container>

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html

@ -18,7 +18,7 @@
<ng-container [formGroup]="fillSettingsFormGroup"> <ng-container [formGroup]="fillSettingsFormGroup">
<div class="tb-form-row column"> <div class="tb-form-row column">
<div class="tb-form-row no-border no-padding space-between"> <div class="tb-form-row no-border no-padding space-between">
<div>{{ title | translate }}</div> <div>{{ titleText | translate }}</div>
<tb-toggle-select formControlName="type"> <tb-toggle-select formControlName="type">
<tb-toggle-option *ngFor="let type of chartFillTypes" [value]="type">{{ chartFillTypeTranslationMap.get(type) | translate }}</tb-toggle-option> <tb-toggle-option *ngFor="let type of chartFillTypes" [value]="type">{{ chartFillTypeTranslationMap.get(type) | translate }}</tb-toggle-option>
</tb-toggle-select> </tb-toggle-select>

2
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts

@ -55,7 +55,7 @@ export class ChartFillSettingsComponent implements OnInit, ControlValueAccessor
disabled: boolean; disabled: boolean;
@Input() @Input()
title = 'widgets.chart.fill'; titleText = 'widgets.chart.fill';
@Input() @Input()
fillNoneTitle = 'widgets.chart.fill-type-none'; fillNoneTitle = 'widgets.chart.fill-type-none';

1
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -487,6 +487,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
} }
if (!this.widgetContext.inited && this.isReady()) { if (!this.widgetContext.inited && this.isReady()) {
this.widgetContext.inited = true; this.widgetContext.inited = true;
this.widgetContext.destroyed = false;
this.dashboardWidget.updateWidgetParams(); this.dashboardWidget.updateWidgetParams();
this.widgetContext.detectContainerChanges(); this.widgetContext.detectContainerChanges();
if (this.cafs.init) { if (this.cafs.init) {

2
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -449,6 +449,8 @@ export class WidgetContext {
labelPattern.destroy(); labelPattern.destroy();
} }
this.labelPatterns.clear(); this.labelPatterns.clear();
this.width = undefined;
this.height = undefined;
this.destroyed = true; this.destroyed = true;
} }

49
ui-ngx/src/app/shared/components/entity/entity-list.component.ts

@ -25,11 +25,17 @@ import {
SimpleChanges, SimpleChanges,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import {
ControlValueAccessor,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormBuilder,
UntypedFormGroup,
ValidationErrors,
Validators
} from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data'; import { BaseData } from '@shared/models/base-data';
@ -49,6 +55,11 @@ import { SubscriptSizing } from '@angular/material/form-field';
provide: NG_VALUE_ACCESSOR, provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => EntityListComponent), useExisting: forwardRef(() => EntityListComponent),
multi: true multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => EntityListComponent),
multi: true
} }
] ]
}) })
@ -56,7 +67,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
entityListFormGroup: UntypedFormGroup; entityListFormGroup: UntypedFormGroup;
modelValue: Array<string> | null; private modelValue: Array<string> | null;
@Input() @Input()
entityType: EntityType; entityType: EntityType;
@ -108,17 +119,16 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(public translate: TranslateService,
public translate: TranslateService,
private entityService: EntityService, private entityService: EntityService,
private fb: UntypedFormBuilder) { private fb: UntypedFormBuilder) {
this.entityListFormGroup = this.fb.group({ this.entityListFormGroup = this.fb.group({
entities: [this.entities, this.required ? [Validators.required] : []], entities: [this.entities],
entity: [null] entity: [null]
}); });
} }
updateValidators() { private updateValidators() {
this.entityListFormGroup.get('entities').setValidators(this.required ? [Validators.required] : []); this.entityListFormGroup.get('entities').setValidators(this.required ? [Validators.required] : []);
this.entityListFormGroup.get('entities').updateValueAndValidity(); this.entityListFormGroup.get('entities').updateValueAndValidity();
} }
@ -189,7 +199,13 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
this.dirty = true; this.dirty = true;
} }
reset() { validate(): ValidationErrors | null {
return this.entityListFormGroup.valid ? null : {
entities: {valid: false}
};
}
private reset() {
this.entities = []; this.entities = [];
this.entityListFormGroup.get('entities').setValue(this.entities); this.entityListFormGroup.get('entities').setValue(this.entities);
this.modelValue = null; this.modelValue = null;
@ -201,7 +217,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
this.dirty = true; this.dirty = true;
} }
add(entity: BaseData<EntityId>): void { private add(entity: BaseData<EntityId>): void {
if (!this.modelValue || this.modelValue.indexOf(entity.id.id) === -1) { if (!this.modelValue || this.modelValue.indexOf(entity.id.id) === -1) {
if (!this.modelValue) { if (!this.modelValue) {
this.modelValue = []; this.modelValue = [];
@ -214,7 +230,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
this.clear(); this.clear();
} }
remove(entity: BaseData<EntityId>) { public remove(entity: BaseData<EntityId>) {
let index = this.entities.indexOf(entity); let index = this.entities.indexOf(entity);
if (index >= 0) { if (index >= 0) {
this.entities.splice(index, 1); this.entities.splice(index, 1);
@ -229,11 +245,11 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
} }
} }
displayEntityFn(entity?: BaseData<EntityId>): string | undefined { public displayEntityFn(entity?: BaseData<EntityId>): string | undefined {
return entity ? entity.name : undefined; return entity ? entity.name : undefined;
} }
fetchEntities(searchText?: string): Observable<Array<BaseData<EntityId>>> { private fetchEntities(searchText?: string): Observable<Array<BaseData<EntityId>>> {
this.searchText = searchText; this.searchText = searchText;
return this.entityService.getEntitiesByNameFilter(this.entityType, searchText, return this.entityService.getEntitiesByNameFilter(this.entityType, searchText,
@ -241,14 +257,14 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
map((data) => data ? data : [])); map((data) => data ? data : []));
} }
onFocus() { public onFocus() {
if (this.dirty) { if (this.dirty) {
this.entityListFormGroup.get('entity').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.entityListFormGroup.get('entity').updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false; this.dirty = false;
} }
} }
clear(value: string = '') { private clear(value: string = '') {
this.entityInput.nativeElement.value = value; this.entityInput.nativeElement.value = value;
this.entityListFormGroup.get('entity').patchValue(value, {emitEvent: true}); this.entityListFormGroup.get('entity').patchValue(value, {emitEvent: true});
setTimeout(() => { setTimeout(() => {
@ -257,8 +273,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV
}, 0); }, 0);
} }
textIsNotEmpty(text: string): boolean { public textIsNotEmpty(text: string): boolean {
return (text && text.length > 0); return (text && text.length > 0);
} }
} }

Loading…
Cancel
Save