Merge pull request #12649 from thingsboard/rc

rc
This commit is contained in:
Viacheslav Klimov 2025-02-10 15:35:43 +02:00 committed by GitHub
commit feeee23fa0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 125 additions and 77 deletions

View File

@ -865,6 +865,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
} }
private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) { private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {
attributeSubscriptions.remove(sessionId);
rpcSubscriptions.remove(sessionId);
notifyTransportAboutClosedSession(sessionId, sessionMd, TransportSessionCloseReason.MAX_CONCURRENT_SESSIONS_LIMIT_REACHED); notifyTransportAboutClosedSession(sessionId, sessionMd, TransportSessionCloseReason.MAX_CONCURRENT_SESSIONS_LIMIT_REACHED);
} }

View File

@ -26,6 +26,7 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
@Slf4j @Slf4j
@ -40,54 +41,63 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor {
if (dashboard == null) { if (dashboard == null) {
throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard");
} }
Set<ShortCustomerInfo> assignedCustomers = null; Set<ShortCustomerInfo> newAssignedCustomers = new HashSet<>(dashboard.getAssignedCustomers());
Dashboard dashboardById = edgeCtx.getDashboardService().findDashboardById(tenantId, dashboardId); Dashboard dashboardById = edgeCtx.getDashboardService().findDashboardById(tenantId, dashboardId);
if (dashboardById == null) { if (dashboardById == null) {
created = true; created = true;
dashboard.setId(null); dashboard.setId(null);
dashboard.setAssignedCustomers(null);
} else { } else {
dashboard.setId(dashboardId); dashboard.setId(dashboardId);
assignedCustomers = filterNonExistingCustomers(tenantId, dashboardById.getAssignedCustomers()); dashboard.setAssignedCustomers(dashboardById.getAssignedCustomers());
} }
dashboardValidator.validate(dashboard, Dashboard::getTenantId); dashboardValidator.validate(dashboard, Dashboard::getTenantId);
if (created) { if (created) {
dashboard.setId(dashboardId); dashboard.setId(dashboardId);
} }
Set<ShortCustomerInfo> msgAssignedCustomers = filterNonExistingCustomers(tenantId, dashboard.getAssignedCustomers());
if (msgAssignedCustomers != null) {
if (assignedCustomers == null) {
assignedCustomers = msgAssignedCustomers;
} else {
assignedCustomers.addAll(msgAssignedCustomers);
}
}
dashboard.setAssignedCustomers(assignedCustomers);
Dashboard savedDashboard = edgeCtx.getDashboardService().saveDashboard(dashboard, false); Dashboard savedDashboard = edgeCtx.getDashboardService().saveDashboard(dashboard, false);
if (msgAssignedCustomers != null && !msgAssignedCustomers.isEmpty()) {
for (ShortCustomerInfo assignedCustomer : msgAssignedCustomers) { updateDashboardAssignments(tenantId, dashboardById, savedDashboard, newAssignedCustomers);
if (assignedCustomer.getCustomerId().equals(customerId)) {
edgeCtx.getDashboardService().assignDashboardToCustomer(tenantId, savedDashboard.getId(), assignedCustomer.getCustomerId());
}
}
} else {
unassignCustomersFromDashboard(tenantId, savedDashboard, customerId);
}
return created; return created;
} }
private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard, CustomerId customerId) { private void updateDashboardAssignments(TenantId tenantId, Dashboard dashboardById, Dashboard savedDashboard, Set<ShortCustomerInfo> newAssignedCustomers) {
if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) { Set<ShortCustomerInfo> currentAssignedCustomers = new HashSet<>();
for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) { if (dashboardById != null) {
if (assignedCustomer.getCustomerId().equals(customerId)) { if (dashboardById.getAssignedCustomers() != null) {
edgeCtx.getDashboardService().unassignDashboardFromCustomer(tenantId, dashboard.getId(), assignedCustomer.getCustomerId()); currentAssignedCustomers.addAll(dashboardById.getAssignedCustomers());
} }
} }
newAssignedCustomers = filterNonExistingCustomers(tenantId, currentAssignedCustomers, newAssignedCustomers);
Set<CustomerId> addedCustomerIds = new HashSet<>();
Set<CustomerId> removedCustomerIds = new HashSet<>();
for (ShortCustomerInfo newAssignedCustomer : newAssignedCustomers) {
if (!savedDashboard.isAssignedToCustomer(newAssignedCustomer.getCustomerId())) {
addedCustomerIds.add(newAssignedCustomer.getCustomerId());
}
}
for (ShortCustomerInfo currentAssignedCustomer : currentAssignedCustomers) {
if (!newAssignedCustomers.contains(currentAssignedCustomer)) {
removedCustomerIds.add(currentAssignedCustomer.getCustomerId());
}
}
for (CustomerId customerIdToAdd : addedCustomerIds) {
edgeCtx.getDashboardService().assignDashboardToCustomer(tenantId, savedDashboard.getId(), customerIdToAdd);
}
for (CustomerId customerIdToRemove : removedCustomerIds) {
edgeCtx.getDashboardService().unassignDashboardFromCustomer(tenantId, savedDashboard.getId(), customerIdToRemove);
} }
} }
protected abstract Dashboard constructDashboardFromUpdateMsg(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg); protected abstract Dashboard constructDashboardFromUpdateMsg(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg);
protected abstract Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> assignedCustomers); protected abstract Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> currentAssignedCustomers, Set<ShortCustomerInfo> newAssignedCustomers);
} }

View File

@ -128,9 +128,9 @@ public abstract class DashboardEdgeProcessor extends BaseDashboardProcessor impl
} }
@Override @Override
protected Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> assignedCustomers) { protected Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> currentAssignedCustomers, Set<ShortCustomerInfo> newAssignedCustomers) {
// do nothing on cloud newAssignedCustomers.addAll(currentAssignedCustomers);
return assignedCustomers; return newAssignedCustomers;
} }
} }

View File

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
@Component @Component
@ -44,7 +45,7 @@ public class DashboardEdgeProcessorV1 extends DashboardEdgeProcessor {
Set<ShortCustomerInfo> assignedCustomers; Set<ShortCustomerInfo> assignedCustomers;
if (dashboardUpdateMsg.hasAssignedCustomers()) { if (dashboardUpdateMsg.hasAssignedCustomers()) {
assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {});
assignedCustomers = filterNonExistingCustomers(tenantId, assignedCustomers); assignedCustomers = filterNonExistingCustomers(tenantId, new HashSet<>(), assignedCustomers);
dashboard.setAssignedCustomers(assignedCustomers); dashboard.setAssignedCustomers(assignedCustomers);
} }
dashboard.setMobileOrder(dashboardUpdateMsg.hasMobileOrder() ? dashboardUpdateMsg.getMobileOrder() : null); dashboard.setMobileOrder(dashboardUpdateMsg.hasMobileOrder() ? dashboardUpdateMsg.getMobileOrder() : null);

View File

@ -53,6 +53,9 @@ public abstract class BaseResourceProcessor extends BaseEdgeProcessor {
} }
String resourceKey = resource.getResourceKey(); String resourceKey = resource.getResourceKey();
ResourceType resourceType = resource.getResourceType(); ResourceType resourceType = resource.getResourceType();
if (!created && !resourceType.isUpdatable()) {
resource.setData(null);
}
PageDataIterable<TbResource> resourcesIterable = new PageDataIterable<>( PageDataIterable<TbResource> resourcesIterable = new PageDataIterable<>(
link -> edgeCtx.getResourceService().findTenantResourcesByResourceTypeAndPageLink(tenantId, resourceType, link), 1024); link -> edgeCtx.getResourceService().findTenantResourcesByResourceTypeAndPageLink(tenantId, resourceType, link), 1024);
for (TbResource tbResource : resourcesIterable) { for (TbResource tbResource : resourcesIterable) {

View File

@ -115,8 +115,9 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(), ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(),
src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted(), src.getRetries(), src.getAdditionalInfo()); src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted(), src.getRetries(), src.getAdditionalInfo());
forwardRpcRequestToDeviceActor(request, response -> { forwardRpcRequestToDeviceActor(request, response -> {
if (src.isRestApiCall()) { String originServiceId = src.getOriginServiceId();
sendRpcResponseToTbCore(src.getOriginServiceId(), response); if (src.isRestApiCall() && originServiceId != null) {
sendRpcResponseToTbCore(originServiceId, response);
} }
consumer.accept(RuleEngineDeviceRpcResponse.builder() consumer.accept(RuleEngineDeviceRpcResponse.builder()
.deviceId(src.getDeviceId()) .deviceId(src.getDeviceId())

View File

@ -16,6 +16,7 @@
package org.thingsboard.server.edge; package org.thingsboard.server.edge;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Sets;
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;
@ -175,7 +176,11 @@ public class DashboardEdgeTest extends AbstractEdgeTest {
@Test @Test
public void testSendDashboardToCloud() throws Exception { public void testSendDashboardToCloud() throws Exception {
Dashboard dashboard = buildDashboardForUplinkMsg(); Customer customer = new Customer();
customer.setTitle("Edge Customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Dashboard dashboard = buildDashboardForUplinkMsg(savedCustomer);
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
DashboardUpdateMsg.Builder dashboardUpdateMsgBuilder = DashboardUpdateMsg.newBuilder(); DashboardUpdateMsg.Builder dashboardUpdateMsgBuilder = DashboardUpdateMsg.newBuilder();
@ -196,6 +201,11 @@ public class DashboardEdgeTest extends AbstractEdgeTest {
Dashboard foundDashboard = doGet("/api/dashboard/" + dashboard.getUuidId(), Dashboard.class); Dashboard foundDashboard = doGet("/api/dashboard/" + dashboard.getUuidId(), Dashboard.class);
Assert.assertNotNull(foundDashboard); Assert.assertNotNull(foundDashboard);
Assert.assertEquals("Edge Test Dashboard", foundDashboard.getName()); Assert.assertEquals("Edge Test Dashboard", foundDashboard.getName());
PageData<DashboardInfo> pageData = doGetTypedWithPageLink("/api/customer/" + savedCustomer.getId().toString() + "/dashboards?",
new TypeReference<>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
Assert.assertEquals("Edge Test Dashboard", pageData.getData().get(0).getTitle());
} }
@Test @Test
@ -242,11 +252,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest {
return savedDashboard; return savedDashboard;
} }
private Dashboard buildDashboardForUplinkMsg() { private Dashboard buildDashboardForUplinkMsg(Customer savedCustomer) {
Dashboard dashboard = new Dashboard(); Dashboard dashboard = new Dashboard();
dashboard.setId(new DashboardId(UUID.randomUUID())); dashboard.setId(new DashboardId(UUID.randomUUID()));
dashboard.setTenantId(tenantId); dashboard.setTenantId(tenantId);
dashboard.setTitle("Edge Test Dashboard"); dashboard.setTitle("Edge Test Dashboard");
dashboard.setAssignedCustomers(Sets.newHashSet(new ShortCustomerInfo(savedCustomer.getId(), savedCustomer.getTitle(), savedCustomer.isPublic())));
return dashboard; return dashboard;
} }

View File

@ -17,6 +17,7 @@ package org.thingsboard.server.edge;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.google.protobuf.AbstractMessage; import com.google.protobuf.AbstractMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
@ -98,30 +99,23 @@ public class ResourceEdgeTest extends AbstractEdgeTest {
public void testSendResourceToCloud() throws Exception { public void testSendResourceToCloud() throws Exception {
TbResource tbResource = createTbResource(); TbResource tbResource = createTbResource();
UUID uuid = Uuids.timeBased(); UUID uuid = Uuids.timeBased();
UplinkMsg uplinkMsg = getUplinkMsg(uuid, tbResource, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); checkResourceOnCloud(uplinkMsg, uuid, tbResource.getTitle());
ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); }
resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(tbResource));
resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder);
uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); @Test
public void testUpdateResourceTitleOnCloud() throws Exception {
TbResource tbResource = createTbResource();
UUID uuid = Uuids.timeBased();
UplinkMsg uplinkMsg = getUplinkMsg(uuid, tbResource, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
edgeImitator.expectResponsesAmount(1); checkResourceOnCloud(uplinkMsg, uuid, tbResource.getTitle());
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses()); tbResource.setTitle("Updated Edge Test Resource");
UplinkMsg updatedUplinkMsg = getUplinkMsg(uuid, tbResource, UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE);
UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); checkResourceOnCloud(updatedUplinkMsg, uuid, tbResource.getTitle());
Assert.assertTrue(latestResponseMsg.getSuccess());
TbResource tb = doGet("/api/resource/" + uuid, TbResource.class);
Assert.assertNotNull(tb);
Assert.assertEquals("Edge Test Resource", tb.getName());
Assert.assertEquals(TEST_DATA, tb.getEncodedData());
} }
@Test @Test
@ -134,21 +128,12 @@ public class ResourceEdgeTest extends AbstractEdgeTest {
UUID uuid = Uuids.timeBased(); UUID uuid = Uuids.timeBased();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); UplinkMsg uplinkMsg = getUplinkMsg(uuid, resource, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder();
resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(resource));
resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder);
uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1); edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); edgeImitator.sendUplinkMsg(uplinkMsg);
Assert.assertTrue(edgeImitator.waitForResponses()); Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
@ -177,4 +162,35 @@ public class ResourceEdgeTest extends AbstractEdgeTest {
tbResource.setEncodedData(TEST_DATA); tbResource.setEncodedData(TEST_DATA);
return tbResource; return tbResource;
} }
private UplinkMsg getUplinkMsg(UUID uuid, TbResource tbResource, UpdateMsgType updateMsgType) throws InvalidProtocolBufferException {
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder();
resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(tbResource));
resourceUpdateMsgBuilder.setMsgType(updateMsgType);
testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder);
uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
return uplinkMsgBuilder.build();
}
private void checkResourceOnCloud(UplinkMsg uplinkMsg, UUID uuid, String resourceTitle) throws Exception {
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsg);
Assert.assertTrue(edgeImitator.waitForResponses());
UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg();
Assert.assertTrue(latestResponseMsg.getSuccess());
TbResource tb = doGet("/api/resource/" + uuid, TbResource.class);
Assert.assertNotNull(tb);
Assert.assertEquals(resourceTitle, tb.getName());
Assert.assertEquals(TEST_DATA, tb.getEncodedData());
}
} }

View File

@ -963,7 +963,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
unSubResults.add((short) MqttReasonCodes.UnsubAck.NO_SUBSCRIPTION_EXISTED.byteValue()); unSubResults.add((short) MqttReasonCodes.UnsubAck.NO_SUBSCRIPTION_EXISTED.byteValue());
} }
} }
if (!activityReported) { if (!activityReported && !deviceSessionCtx.isProvisionOnly()) {
transportService.recordActivity(deviceSessionCtx.getSessionInfo()); transportService.recordActivity(deviceSessionCtx.getSessionInfo());
} }
ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId(), unSubResults)); ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId(), unSubResults));

View File

@ -735,7 +735,11 @@ public class DefaultTransportService extends TransportActivityManager implements
} }
private void recordActivityInternal(TransportProtos.SessionInfoProto sessionInfo) { private void recordActivityInternal(TransportProtos.SessionInfoProto sessionInfo) {
if (sessionInfo != null) {
onActivity(toSessionId(sessionInfo), sessionInfo, getCurrentTimeMillis()); onActivity(toSessionId(sessionInfo), sessionInfo, getCurrentTimeMillis());
} else {
log.warn("Session info is missing, unable to record activity");
}
} }
@Override @Override

View File

@ -22,7 +22,7 @@
[class.!hidden]="isEdit || isDetailsPage"> [class.!hidden]="isEdit || isDetailsPage">
{{'common.open-details-page' | translate }} {{'common.open-details-page' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">

View File

@ -22,13 +22,13 @@
[class.!hidden]="isEdit || isDetailsPage"> [class.!hidden]="isEdit || isDetailsPage">
{{'common.open-details-page' | translate }} {{'common.open-details-page' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'downloadResource')" (click)="onEntityAction($event, 'downloadResource')"
[class.!hidden]="isEdit"> [class.!hidden]="isEdit">
{{ 'javascript.download' | translate }} {{ 'javascript.download' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">

View File

@ -22,13 +22,13 @@
[class.!hidden]="isEdit || isDetailsPage"> [class.!hidden]="isEdit || isDetailsPage">
{{'common.open-details-page' | translate }} {{'common.open-details-page' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'downloadResource')" (click)="onEntityAction($event, 'downloadResource')"
[class.!hidden]="isEdit"> [class.!hidden]="isEdit">
{{ 'resource.download' | translate }} {{ 'resource.download' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">

View File

@ -58,7 +58,7 @@
[class.!hidden]="isEdit || deviceScope !== 'edge'"> [class.!hidden]="isEdit || deviceScope !== 'edge'">
{{ 'edge.unassign-from-edge' | translate }} {{ 'edge.unassign-from-edge' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">

View File

@ -70,7 +70,7 @@
[class.!hidden]="isEdit || edgeScope !== 'tenant'"> [class.!hidden]="isEdit || edgeScope !== 'tenant'">
{{'edge.manage-rulechains' | translate }} {{'edge.manage-rulechains' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">

View File

@ -16,19 +16,19 @@
--> -->
<div class="tb-details-buttons xs:flex xs:flex-col"> <div class="tb-details-buttons xs:flex xs:flex-col">
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'open')" (click)="onEntityAction($event, 'open')"
[class.!hidden]="isEdit || isDetailsPage"> [class.!hidden]="isEdit || isDetailsPage">
{{'common.open-details-page' | translate }} {{'common.open-details-page' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async) || !(entity?.hasData && !entity?.url)" [disabled]="(isLoading$ | async) || !(entity?.hasData && !entity?.url)"
(click)="onEntityAction($event, 'uploadPackage')" (click)="onEntityAction($event, 'uploadPackage')"
[class.!hidden]="isEdit"> [class.!hidden]="isEdit">
{{ 'ota-update.download' | translate }} {{ 'ota-update.download' | translate }}
</button> </button>
<button mat-raised-button color="primary" class="xs:flex-1" <button mat-raised-button color="primary"
[disabled]="(isLoading$ | async)" [disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'delete')" (click)="onEntityAction($event, 'delete')"
[class.!hidden]="hideDelete() || isEdit"> [class.!hidden]="hideDelete() || isEdit">