Merge remote-tracking branch 'origin/hotfix/3.7'
This commit is contained in:
commit
68c8bf8db7
@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import jakarta.validation.ConstraintViolation;
|
import jakarta.validation.ConstraintViolation;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import org.hibernate.exception.ConstraintViolationException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -358,30 +359,34 @@ public abstract class BaseController {
|
|||||||
|
|
||||||
private ThingsboardException handleException(Exception exception, boolean logException) {
|
private ThingsboardException handleException(Exception exception, boolean logException) {
|
||||||
if (logException && logControllerErrorStackTrace) {
|
if (logException && logControllerErrorStackTrace) {
|
||||||
log.error("Error [{}]", exception.getMessage(), exception);
|
try {
|
||||||
}
|
SecurityUser user = getCurrentUser();
|
||||||
|
log.error("[{}][{}] Error", user.getTenantId(), user.getId(), exception);
|
||||||
String cause = "";
|
} catch (Exception e) {
|
||||||
if (exception.getCause() != null) {
|
log.error("Error", exception);
|
||||||
cause = exception.getCause().getClass().getCanonicalName();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Throwable cause = exception.getCause();
|
||||||
if (exception instanceof ThingsboardException) {
|
if (exception instanceof ThingsboardException) {
|
||||||
return (ThingsboardException) exception;
|
return (ThingsboardException) exception;
|
||||||
} else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException
|
} else if (exception instanceof IllegalArgumentException || exception instanceof IncorrectParameterException
|
||||||
|| exception instanceof DataValidationException || cause.contains("IncorrectParameterException")) {
|
|| exception instanceof DataValidationException || cause instanceof IncorrectParameterException) {
|
||||||
return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
return new ThingsboardException(exception.getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||||
} else if (exception instanceof MessagingException) {
|
} else if (exception instanceof MessagingException) {
|
||||||
return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL);
|
return new ThingsboardException("Unable to send mail: " + exception.getMessage(), ThingsboardErrorCode.GENERAL);
|
||||||
} else if (exception instanceof AsyncRequestTimeoutException) {
|
} else if (exception instanceof AsyncRequestTimeoutException) {
|
||||||
return new ThingsboardException("Request timeout", ThingsboardErrorCode.GENERAL);
|
return new ThingsboardException("Request timeout", ThingsboardErrorCode.GENERAL);
|
||||||
} else if (exception instanceof DataAccessException) {
|
} else if (exception instanceof DataAccessException) {
|
||||||
String errorType = exception.getClass().getSimpleName();
|
|
||||||
if (!logControllerErrorStackTrace) { // not to log the error twice
|
if (!logControllerErrorStackTrace) { // not to log the error twice
|
||||||
log.warn("Database error: {} - {}", errorType, ExceptionUtils.getRootCauseMessage(exception));
|
log.warn("Database error: {} - {}", exception.getClass().getSimpleName(), ExceptionUtils.getRootCauseMessage(exception));
|
||||||
}
|
}
|
||||||
|
if (cause instanceof ConstraintViolationException) {
|
||||||
|
return new ThingsboardException(ExceptionUtils.getRootCause(exception).getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||||
|
} else {
|
||||||
return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL);
|
return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL);
|
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -204,6 +204,9 @@ public class EdgeEventSourcingListener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (entity instanceof OAuth2Info oAuth2Info) {
|
||||||
|
return oAuth2Info.isEdgeEnabled();
|
||||||
|
}
|
||||||
// Default: If the entity doesn't match any of the conditions, consider it as valid.
|
// Default: If the entity doesn't match any of the conditions, consider it as valid.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,8 +45,11 @@ public class OAuth2EdgeEventFetcher implements EdgeEventFetcher {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
|
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
|
||||||
List<EdgeEvent> result = new ArrayList<>();
|
|
||||||
OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info();
|
OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info();
|
||||||
|
if (!oAuth2Info.isEdgeEnabled()) {
|
||||||
|
return new PageData<>();
|
||||||
|
}
|
||||||
|
List<EdgeEvent> result = new ArrayList<>();
|
||||||
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2,
|
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2,
|
||||||
EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info)));
|
EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info)));
|
||||||
// returns PageData object to be in sync with other fetchers
|
// returns PageData object to be in sync with other fetchers
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public class OAuth2EdgeProcessor extends BaseEdgeProcessor {
|
|||||||
public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) {
|
public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) {
|
||||||
DownlinkMsg downlinkMsg = null;
|
DownlinkMsg downlinkMsg = null;
|
||||||
OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class);
|
OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class);
|
||||||
if (oAuth2Info != null) {
|
if (oAuth2Info != null && oAuth2Info.isEdgeEnabled()) {
|
||||||
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info);
|
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info);
|
||||||
downlinkMsg = DownlinkMsg.newBuilder()
|
downlinkMsg = DownlinkMsg.newBuilder()
|
||||||
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
|
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
|
||||||
|
|||||||
@ -24,12 +24,14 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.server.cluster.TbClusterService;
|
import org.thingsboard.server.cluster.TbClusterService;
|
||||||
import org.thingsboard.server.common.data.ApiUsageState;
|
import org.thingsboard.server.common.data.ApiUsageState;
|
||||||
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.EdgeUtils;
|
import org.thingsboard.server.common.data.EdgeUtils;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.HasName;
|
import org.thingsboard.server.common.data.HasName;
|
||||||
import org.thingsboard.server.common.data.HasRuleEngineProfile;
|
import org.thingsboard.server.common.data.HasRuleEngineProfile;
|
||||||
|
import org.thingsboard.server.common.data.ResourceType;
|
||||||
import org.thingsboard.server.common.data.TbResourceInfo;
|
import org.thingsboard.server.common.data.TbResourceInfo;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.TenantProfile;
|
import org.thingsboard.server.common.data.TenantProfile;
|
||||||
@ -340,6 +342,7 @@ public class DefaultTbClusterService implements TbClusterService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResourceChange(TbResourceInfo resource, TbQueueCallback callback) {
|
public void onResourceChange(TbResourceInfo resource, TbQueueCallback callback) {
|
||||||
|
if (resource.getResourceType() == ResourceType.LWM2M_MODEL) {
|
||||||
TenantId tenantId = resource.getTenantId();
|
TenantId tenantId = resource.getTenantId();
|
||||||
log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceKey());
|
log.trace("[{}][{}][{}] Processing change resource", tenantId, resource.getResourceType(), resource.getResourceKey());
|
||||||
TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder()
|
TransportProtos.ResourceUpdateMsg resourceUpdateMsg = TransportProtos.ResourceUpdateMsg.newBuilder()
|
||||||
@ -349,20 +352,23 @@ public class DefaultTbClusterService implements TbClusterService {
|
|||||||
.setResourceKey(resource.getResourceKey())
|
.setResourceKey(resource.getResourceKey())
|
||||||
.build();
|
.build();
|
||||||
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceUpdateMsg(resourceUpdateMsg).build();
|
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceUpdateMsg(resourceUpdateMsg).build();
|
||||||
broadcast(transportMsg, callback);
|
broadcast(transportMsg, DataConstants.LWM2M_TRANSPORT_NAME, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResourceDeleted(TbResourceInfo resource, TbQueueCallback callback) {
|
public void onResourceDeleted(TbResourceInfo resource, TbQueueCallback callback) {
|
||||||
log.trace("[{}] Processing delete resource", resource);
|
if (resource.getResourceType() == ResourceType.LWM2M_MODEL) {
|
||||||
TransportProtos.ResourceDeleteMsg resourceUpdateMsg = TransportProtos.ResourceDeleteMsg.newBuilder()
|
log.trace("[{}][{}][{}] Processing delete resource", resource.getTenantId(), resource.getResourceType(), resource.getResourceKey());
|
||||||
|
TransportProtos.ResourceDeleteMsg resourceDeleteMsg = TransportProtos.ResourceDeleteMsg.newBuilder()
|
||||||
.setTenantIdMSB(resource.getTenantId().getId().getMostSignificantBits())
|
.setTenantIdMSB(resource.getTenantId().getId().getMostSignificantBits())
|
||||||
.setTenantIdLSB(resource.getTenantId().getId().getLeastSignificantBits())
|
.setTenantIdLSB(resource.getTenantId().getId().getLeastSignificantBits())
|
||||||
.setResourceType(resource.getResourceType().name())
|
.setResourceType(resource.getResourceType().name())
|
||||||
.setResourceKey(resource.getResourceKey())
|
.setResourceKey(resource.getResourceKey())
|
||||||
.build();
|
.build();
|
||||||
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceDeleteMsg(resourceUpdateMsg).build();
|
ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setResourceDeleteMsg(resourceDeleteMsg).build();
|
||||||
broadcast(transportMsg, callback);
|
broadcast(transportMsg, DataConstants.LWM2M_TRANSPORT_NAME, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void broadcastEntityChangeToTransport(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
|
private <T> void broadcastEntityChangeToTransport(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
|
||||||
@ -384,8 +390,19 @@ public class DefaultTbClusterService implements TbClusterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void broadcast(ToTransportMsg transportMsg, TbQueueCallback callback) {
|
private void broadcast(ToTransportMsg transportMsg, TbQueueCallback callback) {
|
||||||
TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer();
|
|
||||||
Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT);
|
Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT);
|
||||||
|
broadcast(transportMsg, tbTransportServices, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcast(ToTransportMsg transportMsg, String transportType, TbQueueCallback callback) {
|
||||||
|
Set<String> tbTransportServices = partitionService.getAllServices(ServiceType.TB_TRANSPORT).stream()
|
||||||
|
.filter(info -> info.getTransportsList().contains(transportType))
|
||||||
|
.map(TransportProtos.ServiceInfo::getServiceId).collect(Collectors.toSet());
|
||||||
|
broadcast(transportMsg, tbTransportServices, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcast(ToTransportMsg transportMsg, Set<String> tbTransportServices, TbQueueCallback callback) {
|
||||||
|
TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer();
|
||||||
TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null;
|
TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null;
|
||||||
for (String transportServiceId : tbTransportServices) {
|
for (String transportServiceId : tbTransportServices) {
|
||||||
TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
|
TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
|
||||||
|
|||||||
@ -27,13 +27,16 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.common.data.Customer;
|
import org.thingsboard.server.common.data.Customer;
|
||||||
import org.thingsboard.server.common.data.Dashboard;
|
import org.thingsboard.server.common.data.Dashboard;
|
||||||
import org.thingsboard.server.common.data.DashboardInfo;
|
import org.thingsboard.server.common.data.DashboardInfo;
|
||||||
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.ShortCustomerInfo;
|
import org.thingsboard.server.common.data.ShortCustomerInfo;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
|
import org.thingsboard.server.common.data.asset.AssetProfile;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
import org.thingsboard.server.common.data.audit.ActionType;
|
||||||
import org.thingsboard.server.common.data.edge.Edge;
|
import org.thingsboard.server.common.data.edge.Edge;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
@ -48,6 +51,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@ -547,6 +551,33 @@ public class DashboardControllerTest extends AbstractControllerTest {
|
|||||||
testEntityDaoWithRelationsTransactionalException(dashboardDao, savedTenant.getId(), dashboardId, "/api/dashboard/" + dashboardId);
|
testEntityDaoWithRelationsTransactionalException(dashboardDao, savedTenant.getId(), dashboardId, "/api/dashboard/" + dashboardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenDeletingDashboard_ifReferencedByDeviceProfile_thenReturnError() throws Exception {
|
||||||
|
Dashboard dashboard = createDashboard("test");
|
||||||
|
DeviceProfile deviceProfile = createDeviceProfile("test");
|
||||||
|
deviceProfile.setDefaultDashboardId(dashboard.getId());
|
||||||
|
doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
|
||||||
|
|
||||||
|
String response = doDelete("/api/dashboard/" + dashboard.getUuidId()).andExpect(status().isBadRequest())
|
||||||
|
.andReturn().getResponse().getContentAsString();
|
||||||
|
String errorMessage = JacksonUtil.toJsonNode(response).get("message").asText();
|
||||||
|
assertThat(errorMessage).containsIgnoringCase("referenced by a device profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenDeletingDashboard_ifReferencedByAssetProfile_thenReturnError() throws Exception {
|
||||||
|
Dashboard dashboard = createDashboard("test");
|
||||||
|
AssetProfile assetProfile = createAssetProfile("test");
|
||||||
|
assetProfile.setDefaultDashboardId(dashboard.getId());
|
||||||
|
doPost("/api/assetProfile", assetProfile, AssetProfile.class);
|
||||||
|
|
||||||
|
String response = doDelete("/api/dashboard/" + dashboard.getUuidId()).andExpect(status().isBadRequest())
|
||||||
|
.andReturn().getResponse().getContentAsString();
|
||||||
|
String errorMessage = JacksonUtil.toJsonNode(response).get("message").asText();
|
||||||
|
assertThat(errorMessage).containsIgnoringCase("referenced by an asset profile");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Dashboard createDashboard(String title) {
|
private Dashboard createDashboard(String title) {
|
||||||
Dashboard dashboard = new Dashboard();
|
Dashboard dashboard = new Dashboard();
|
||||||
dashboard.setTitle(title);
|
dashboard.setTitle(title);
|
||||||
|
|||||||
@ -616,6 +616,14 @@ public class TenantControllerTest extends AbstractControllerTest {
|
|||||||
assertThat(usedTpi.getTopic()).isEqualTo(DataConstants.HP_QUEUE_TOPIC);
|
assertThat(usedTpi.getTopic()).isEqualTo(DataConstants.HP_QUEUE_TOPIC);
|
||||||
assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID);
|
assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID);
|
||||||
});
|
});
|
||||||
|
assertThat(partitionService.resolve(ServiceType.TB_RULE_ENGINE, null, tenantId, tenantId)).satisfies(tpi -> {
|
||||||
|
assertThat(tpi.getTopic()).isEqualTo(MAIN_QUEUE_TOPIC);
|
||||||
|
assertThat(tpi.getTenantId()).get().isEqualTo(tenantId);
|
||||||
|
});
|
||||||
|
assertThat(partitionService.resolve(ServiceType.TB_RULE_ENGINE, "", tenantId, tenantId)).satisfies(tpi -> {
|
||||||
|
assertThat(tpi.getTopic()).isEqualTo(MAIN_QUEUE_TOPIC);
|
||||||
|
assertThat(tpi.getTenantId()).get().isEqualTo(tenantId);
|
||||||
|
});
|
||||||
|
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
tenantProfile.setIsolatedTbRuleEngine(true);
|
tenantProfile.setIsolatedTbRuleEngine(true);
|
||||||
@ -850,4 +858,5 @@ public class TenantControllerTest extends AbstractControllerTest {
|
|||||||
testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant()));
|
testBroadcastEntityStateChangeEventNever(createEntityId_NULL_UUID(new Tenant()));
|
||||||
Mockito.reset(tbClusterService);
|
Mockito.reset(tbClusterService);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ package org.thingsboard.server.queue.discovery;
|
|||||||
|
|
||||||
import com.google.common.hash.HashFunction;
|
import com.google.common.hash.HashFunction;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -36,7 +37,6 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
|
|||||||
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
|
import org.thingsboard.server.queue.discovery.event.ServiceListChangedEvent;
|
||||||
import org.thingsboard.server.queue.util.AfterStartUp;
|
import org.thingsboard.server.queue.util.AfterStartUp;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -319,7 +319,7 @@ public class HashPartitionService implements PartitionService {
|
|||||||
|
|
||||||
private QueueKey getQueueKey(ServiceType serviceType, String queueName, TenantId tenantId) {
|
private QueueKey getQueueKey(ServiceType serviceType, String queueName, TenantId tenantId) {
|
||||||
TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId);
|
TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId);
|
||||||
if (queueName == null) {
|
if (queueName == null || queueName.isEmpty()) {
|
||||||
queueName = MAIN_QUEUE_NAME;
|
queueName = MAIN_QUEUE_NAME;
|
||||||
}
|
}
|
||||||
QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId);
|
QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId);
|
||||||
@ -672,6 +672,7 @@ public class HashPartitionService implements PartitionService {
|
|||||||
public QueueConfig(QueueRoutingInfo queueRoutingInfo) {
|
public QueueConfig(QueueRoutingInfo queueRoutingInfo) {
|
||||||
this.duplicateMsgToAllPartitions = queueRoutingInfo.isDuplicateMsgToAllPartitions();
|
this.duplicateMsgToAllPartitions = queueRoutingInfo.isDuplicateMsgToAllPartitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -948,7 +948,7 @@ public class DefaultTransportService extends TransportActivityManager implements
|
|||||||
String resourceId = msg.getResourceKey();
|
String resourceId = msg.getResourceKey();
|
||||||
transportResourceCache.evict(tenantId, resourceType, resourceId);
|
transportResourceCache.evict(tenantId, resourceType, resourceId);
|
||||||
sessions.forEach((id, mdRez) -> {
|
sessions.forEach((id, mdRez) -> {
|
||||||
log.warn("ResourceDelete - [{}] [{}]", id, mdRez);
|
log.trace("ResourceDelete - [{}] [{}]", id, mdRez);
|
||||||
transportCallbackExecutor.submit(() -> mdRez.getListener().onResourceDelete(msg));
|
transportCallbackExecutor.submit(() -> mdRez.getListener().onResourceDelete(msg));
|
||||||
});
|
});
|
||||||
} else if (toSessionMsg.getQueueUpdateMsgsCount() > 0) {
|
} else if (toSessionMsg.getQueueUpdateMsgsCount() > 0) {
|
||||||
|
|||||||
@ -18,7 +18,6 @@ package org.thingsboard.server.dao.dashboard;
|
|||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.hibernate.exception.ConstraintViolationException;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -57,6 +56,7 @@ import org.thingsboard.server.dao.service.Validator;
|
|||||||
import org.thingsboard.server.dao.sql.JpaExecutorService;
|
import org.thingsboard.server.dao.sql.JpaExecutorService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.thingsboard.server.dao.service.Validator.validateId;
|
import static org.thingsboard.server.dao.service.Validator.validateId;
|
||||||
@ -235,13 +235,12 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
|
|||||||
publishEvictEvent(new DashboardTitleEvictEvent(dashboardId));
|
publishEvictEvent(new DashboardTitleEvictEvent(dashboardId));
|
||||||
countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD);
|
countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD);
|
||||||
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(dashboardId).build());
|
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(dashboardId).build());
|
||||||
} catch (Exception t) {
|
} catch (Exception e) {
|
||||||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
|
checkConstraintViolation(e, Map.of(
|
||||||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_dashboard_device_profile")) {
|
"fk_default_dashboard_device_profile", "The dashboard is referenced by a device profile",
|
||||||
throw new DataValidationException("The dashboard referenced by the device profiles cannot be deleted!");
|
"fk_default_dashboard_asset_profile", "The dashboard is referenced by an asset profile"
|
||||||
} else {
|
));
|
||||||
throw t;
|
throw e;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -181,9 +181,12 @@ public class CalculateDeltaNode implements TbNode {
|
|||||||
|
|
||||||
private ListenableFuture<ValueWithTs> getLatestFromCacheOrFetchFromDb(TbContext ctx, TbMsg msg) {
|
private ListenableFuture<ValueWithTs> getLatestFromCacheOrFetchFromDb(TbContext ctx, TbMsg msg) {
|
||||||
EntityId originator = msg.getOriginator();
|
EntityId originator = msg.getOriginator();
|
||||||
|
if (config.isUseCache()) {
|
||||||
ValueWithTs valueWithTs = cache.get(msg.getOriginator());
|
ValueWithTs valueWithTs = cache.get(msg.getOriginator());
|
||||||
return valueWithTs != null ? Futures.immediateFuture(valueWithTs) : fetchLatestValueAsync(ctx, originator);
|
return valueWithTs != null ? Futures.immediateFuture(valueWithTs) : fetchLatestValueAsync(ctx, originator);
|
||||||
}
|
}
|
||||||
|
return fetchLatestValueAsync(ctx, originator);
|
||||||
|
}
|
||||||
|
|
||||||
private record ValueWithTs(long ts, double value) {
|
private record ValueWithTs(long ts, double value) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
type = ComponentType.EXTERNAL,
|
type = ComponentType.EXTERNAL,
|
||||||
name = "mqtt",
|
name = "mqtt",
|
||||||
configClazz = TbMqttNodeConfiguration.class,
|
configClazz = TbMqttNodeConfiguration.class,
|
||||||
|
version = 1,
|
||||||
clusteringMode = ComponentClusteringMode.USER_PREFERENCE,
|
clusteringMode = ComponentClusteringMode.USER_PREFERENCE,
|
||||||
nodeDescription = "Publish messages to the MQTT broker",
|
nodeDescription = "Publish messages to the MQTT broker",
|
||||||
nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
|
nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
|
||||||
|
|||||||
@ -109,7 +109,6 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
public void setUp() throws TbNodeException {
|
public void setUp() throws TbNodeException {
|
||||||
config = new CalculateDeltaNodeConfiguration().defaultConfiguration();
|
config = new CalculateDeltaNodeConfiguration().defaultConfiguration();
|
||||||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
||||||
node.init(ctxMock, nodeConfiguration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -166,8 +165,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() {
|
public void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
var msgData = "{\"pulseCounter\": 42}";
|
var msgData = "{\"pulseCounter\": 42}";
|
||||||
var msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData);
|
var msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData);
|
||||||
|
|
||||||
@ -181,8 +181,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() {
|
public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY);
|
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY);
|
||||||
|
|
||||||
// WHEN
|
// WHEN
|
||||||
@ -196,8 +197,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() {
|
public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT);
|
var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT);
|
||||||
|
|
||||||
// WHEN
|
// WHEN
|
||||||
@ -451,8 +453,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenInvalidStringValue_whenOnMsg_thenException() {
|
public void givenInvalidStringValue_whenOnMsg_thenException() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high")));
|
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high")));
|
||||||
|
|
||||||
var msgData = "{\"pulseCounter\":\"123\"}";
|
var msgData = "{\"pulseCounter\":\"123\"}";
|
||||||
@ -475,8 +478,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenBooleanValue_whenOnMsg_thenException() {
|
public void givenBooleanValue_whenOnMsg_thenException() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false)));
|
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false)));
|
||||||
|
|
||||||
var msgData = "{\"pulseCounter\":true}";
|
var msgData = "{\"pulseCounter\":true}";
|
||||||
@ -499,8 +503,9 @@ public class CalculateDeltaNodeTest extends AbstractRuleNodeUpgradeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenJsonValue_whenOnMsg_thenException() {
|
public void givenJsonValue_whenOnMsg_thenException() throws TbNodeException {
|
||||||
// GIVEN
|
// GIVEN
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}")));
|
mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}")));
|
||||||
|
|
||||||
var msgData = "{\"pulseCounter\":{\"isActive\":true}}";
|
var msgData = "{\"pulseCounter\":{\"isActive\":true}}";
|
||||||
|
|||||||
@ -364,6 +364,16 @@ export function mergeDeep<T>(target: T, ...sources: T[]): T {
|
|||||||
return _.merge(target, ...sources);
|
return _.merge(target, ...sources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ignoreArrayMergeFunc(target: any, sources: any) {
|
||||||
|
if (_.isArray(target)) {
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeDeepIgnoreArray<T>(target: T, ...sources: T[]): T {
|
||||||
|
return _.mergeWith(target, ...sources, ignoreArrayMergeFunc);
|
||||||
|
}
|
||||||
|
|
||||||
export function guid(): string {
|
export function guid(): string {
|
||||||
function s4(): string {
|
function s4(): string {
|
||||||
return Math.floor((1 + Math.random()) * 0x10000)
|
return Math.floor((1 + Math.random()) * 0x10000)
|
||||||
|
|||||||
@ -33,7 +33,7 @@ import {
|
|||||||
getTimewindowConfig,
|
getTimewindowConfig,
|
||||||
setTimewindowConfig
|
setTimewindowConfig
|
||||||
} from '@home/components/widget/config/timewindow-config-panel.component';
|
} from '@home/components/widget/config/timewindow-config-panel.component';
|
||||||
import { formatValue, isUndefined, mergeDeep } from '@core/utils';
|
import { formatValue, isUndefined, mergeDeepIgnoreArray } from '@core/utils';
|
||||||
import {
|
import {
|
||||||
cssSizeToStrSize,
|
cssSizeToStrSize,
|
||||||
DateFormatProcessor,
|
DateFormatProcessor,
|
||||||
@ -117,7 +117,7 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected onConfigSet(configData: WidgetConfigComponentData) {
|
protected onConfigSet(configData: WidgetConfigComponentData) {
|
||||||
const settings: RangeChartWidgetSettings = mergeDeep<RangeChartWidgetSettings>({} as RangeChartWidgetSettings,
|
const settings: RangeChartWidgetSettings = mergeDeepIgnoreArray<RangeChartWidgetSettings>({} as RangeChartWidgetSettings,
|
||||||
rangeChartDefaultSettings, configData.config.settings as RangeChartWidgetSettings);
|
rangeChartDefaultSettings, configData.config.settings as RangeChartWidgetSettings);
|
||||||
const iconSize = resolveCssSize(configData.config.iconSize);
|
const iconSize = resolveCssSize(configData.config.iconSize);
|
||||||
this.rangeChartWidgetConfigForm = this.fb.group({
|
this.rangeChartWidgetConfigForm = this.fb.group({
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import {
|
|||||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppState } from '@core/core.state';
|
import { AppState } from '@core/core.state';
|
||||||
import { formatValue, mergeDeep } from '@core/utils';
|
import { formatValue, mergeDeepIgnoreArray } from '@core/utils';
|
||||||
import {
|
import {
|
||||||
rangeChartDefaultSettings,
|
rangeChartDefaultSettings,
|
||||||
RangeChartWidgetSettings
|
RangeChartWidgetSettings
|
||||||
@ -99,7 +99,7 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected defaultSettings(): WidgetSettings {
|
protected defaultSettings(): WidgetSettings {
|
||||||
return mergeDeep<RangeChartWidgetSettings>({} as RangeChartWidgetSettings, rangeChartDefaultSettings);
|
return mergeDeepIgnoreArray<RangeChartWidgetSettings>({} as RangeChartWidgetSettings, rangeChartDefaultSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onSettingsSet(settings: WidgetSettings) {
|
protected onSettingsSet(settings: WidgetSettings) {
|
||||||
|
|||||||
@ -139,7 +139,7 @@ export class ColorRangeListComponent implements OnInit, ControlValueAccessor, On
|
|||||||
} else {
|
} else {
|
||||||
rangeList = deepClone(value);
|
rangeList = deepClone(value);
|
||||||
}
|
}
|
||||||
this.colorRangeListFormGroup.get('advancedMode').patchValue(rangeList.advancedMode, {emitEvent: false});
|
this.colorRangeListFormGroup.get('advancedMode').patchValue(rangeList.advancedMode || false, {emitEvent: false});
|
||||||
if (isDefinedAndNotNull(rangeList?.range)) {
|
if (isDefinedAndNotNull(rangeList?.range)) {
|
||||||
rangeList.range.forEach((r) => this.rangeListFormArray.push(this.colorRangeControl(r), {emitEvent: false}));
|
rangeList.range.forEach((r) => this.rangeListFormArray.push(this.colorRangeControl(r), {emitEvent: false}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { PageComponent } from '@shared/components/page.component';
|
import { PageComponent } from '@shared/components/page.component';
|
||||||
import { ColorRange } from '@shared/models/widget-settings.models';
|
import { ColorRange, ColorRangeSettings } from '@shared/models/widget-settings.models';
|
||||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
@ -71,8 +71,8 @@ export class ColorRangePanelComponent extends PageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyColorRangeSettings() {
|
applyColorRangeSettings() {
|
||||||
const colorRangeSettings = this.colorRangeFormGroup.get('rangeList').value;
|
const colorRangeSettings: ColorRangeSettings = this.colorRangeFormGroup.get('rangeList').value;
|
||||||
this.colorRangeApplied.emit(colorRangeSettings);
|
this.colorRangeApplied.emit(colorRangeSettings.range);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import {
|
|||||||
ViewContainerRef
|
ViewContainerRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { ColorRange, ComponentStyle } from '@shared/models/widget-settings.models';
|
import { ColorRange, ColorRangeSettings, ComponentStyle } from '@shared/models/widget-settings.models';
|
||||||
import { MatButton } from '@angular/material/button';
|
import { MatButton } from '@angular/material/button';
|
||||||
import { TbPopoverService } from '@shared/components/popover.service';
|
import { TbPopoverService } from '@shared/components/popover.service';
|
||||||
import { ColorRangePanelComponent } from '@home/components/widget/lib/settings/common/color-range-panel.component';
|
import { ColorRangePanelComponent } from '@home/components/widget/lib/settings/common/color-range-panel.component';
|
||||||
@ -108,8 +108,8 @@ export class ColorRangeSettingsComponent implements OnInit, ControlValueAccessor
|
|||||||
this.updateColorStyle();
|
this.updateColorStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(value: Array<ColorRange>): void {
|
writeValue(value: Array<ColorRange> | ColorRangeSettings): void {
|
||||||
this.modelValue = value;
|
this.modelValue = Array.isArray(value) ? value : value.range;
|
||||||
this.updateColorStyle();
|
this.updateColorStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ export class ColorRangeSettingsComponent implements OnInit, ControlValueAccessor
|
|||||||
{},
|
{},
|
||||||
{}, {}, true);
|
{}, {}, true);
|
||||||
colorRangeSettingsPanelPopover.tbComponentRef.instance.popover = colorRangeSettingsPanelPopover;
|
colorRangeSettingsPanelPopover.tbComponentRef.instance.popover = colorRangeSettingsPanelPopover;
|
||||||
colorRangeSettingsPanelPopover.tbComponentRef.instance.colorRangeApplied.subscribe((colorRangeSettings) => {
|
colorRangeSettingsPanelPopover.tbComponentRef.instance.colorRangeApplied.subscribe((colorRangeSettings: Array<ColorRange>) => {
|
||||||
colorRangeSettingsPanelPopover.hide();
|
colorRangeSettingsPanelPopover.hide();
|
||||||
this.modelValue = colorRangeSettings;
|
this.modelValue = colorRangeSettings;
|
||||||
this.updateColorStyle();
|
this.updateColorStyle();
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
<div class="tb-json-content" style="background: #fff;" [ngClass]="{'fill-height': fillHeight}"
|
<div class="tb-json-content" style="background: #fff;" [ngClass]="{'fill-height': fillHeight}"
|
||||||
tb-fullscreen
|
tb-fullscreen
|
||||||
[fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
|
[fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
|
||||||
<div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" class="tb-json-content-toolbar" *ngIf="hideToolbar">
|
<div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" class="tb-json-content-toolbar" *ngIf="!hideToolbar">
|
||||||
<label class="tb-title no-padding" [ngClass]="{'tb-error': !disabled && (!contentValid || required && !contentBody), 'tb-required': !disabled && required}">{{ label }}</label>
|
<label class="tb-title no-padding" [ngClass]="{'tb-error': !disabled && (!contentValid || required && !contentBody), 'tb-required': !disabled && required}">{{ label }}</label>
|
||||||
<span fxFlex></span>
|
<span fxFlex></span>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
|
|||||||
@ -655,7 +655,7 @@ export enum SnmpAuthenticationProtocol {
|
|||||||
SHA_256 = 'SHA_256',
|
SHA_256 = 'SHA_256',
|
||||||
SHA_384 = 'SHA_384',
|
SHA_384 = 'SHA_384',
|
||||||
SHA_512 = 'SHA_512',
|
SHA_512 = 'SHA_512',
|
||||||
MD5 = 'MD%'
|
MD5 = 'MD5'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SnmpAuthenticationProtocolTranslationMap = new Map<SnmpAuthenticationProtocol, string>([
|
export const SnmpAuthenticationProtocolTranslationMap = new Map<SnmpAuthenticationProtocol, string>([
|
||||||
|
|||||||
@ -195,8 +195,8 @@ export const colorRangeIncludes = (range: ColorRange, toCheck: ColorRange): bool
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filterIncludingColorRanges = (ranges: Array<ColorRange>): Array<ColorRange> => {
|
export const filterIncludingColorRanges = (ranges: Array<ColorRange> | ColorRangeSettings): Array<ColorRange> => {
|
||||||
const result = [...ranges];
|
const result = [...(Array.isArray(ranges) ? ranges : ranges.range)];
|
||||||
let includes = true;
|
let includes = true;
|
||||||
while (includes) {
|
while (includes) {
|
||||||
let index = -1;
|
let index = -1;
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import { AbstractControl, UntypedFormGroup } from '@angular/forms';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Dashboard } from '@shared/models/dashboard.models';
|
import { Dashboard } from '@shared/models/dashboard.models';
|
||||||
import { IAliasController } from '@core/api/widget-api.models';
|
import { IAliasController } from '@core/api/widget-api.models';
|
||||||
import { isNotEmptyStr, mergeDeep } from '@core/utils';
|
import { isNotEmptyStr, mergeDeepIgnoreArray } from '@core/utils';
|
||||||
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
|
import { WidgetConfigComponentData } from '@home/models/widget-component.models';
|
||||||
import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
|
import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models';
|
||||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||||
@ -878,7 +878,7 @@ export abstract class WidgetSettingsComponent extends PageComponent implements
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
this.settingsValue = this.defaultSettings();
|
this.settingsValue = this.defaultSettings();
|
||||||
} else {
|
} else {
|
||||||
this.settingsValue = mergeDeep(this.defaultSettings(), value);
|
this.settingsValue = mergeDeepIgnoreArray(this.defaultSettings(), value);
|
||||||
}
|
}
|
||||||
if (!this.settingsSet) {
|
if (!this.settingsSet) {
|
||||||
this.settingsSet = true;
|
this.settingsSet = true;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user