Browse Source

Add customerId to alarm

pull/4563/head
Igor Kulikov 5 years ago
parent
commit
3094fb7240
  1. 5
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  2. 117
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  3. 16
      application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java
  4. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityService.java
  5. 6
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  6. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  7. 46
      dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
  8. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  9. 12
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java
  10. 5
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java
  11. 136
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  12. 9
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  13. 16
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  14. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  15. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java
  16. 1
      dao/src/main/resources/sql/schema-entities-hsql.sql
  17. 1
      dao/src/main/resources/sql/schema-entities.sql
  18. 6
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

5
application/src/main/data/upgrade/3.2.2/schema_update.sql

@ -90,6 +90,11 @@ ALTER TABLE device
ADD COLUMN IF NOT EXISTS firmware_id uuid, ADD COLUMN IF NOT EXISTS firmware_id uuid,
ADD COLUMN IF NOT EXISTS software_id uuid; ADD COLUMN IF NOT EXISTS software_id uuid;
ALTER TABLE alarm
ADD COLUMN IF NOT EXISTS customer_id uuid;
DELETE FROM relation WHERE from_type = 'TENANT' AND relation_type_group = 'RULE_CHAIN';
DO $$ DO $$
BEGIN BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN

117
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -27,6 +27,9 @@ import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
@ -34,9 +37,13 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
@ -72,6 +79,15 @@ public class DefaultDataUpdateService implements DataUpdateService {
@Autowired @Autowired
private TimeseriesService tsService; private TimeseriesService tsService;
@Autowired
private AlarmService alarmService;
@Autowired
private EntityService entityService;
@Autowired
private AlarmDao alarmDao;
@Override @Override
public void updateData(String fromVersion) throws Exception { public void updateData(String fromVersion) throws Exception {
switch (fromVersion) { switch (fromVersion) {
@ -90,14 +106,25 @@ public class DefaultDataUpdateService implements DataUpdateService {
case "3.2.2": case "3.2.2":
log.info("Updating data from version 3.2.2 to 3.3.0 ..."); log.info("Updating data from version 3.2.2 to 3.3.0 ...");
tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); tenantsDefaultEdgeRuleChainUpdater.updateEntities(null);
tenantsAlarmsCustomerUpdater.updateEntities(null);
break; break;
default: default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
} }
} }
private PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater = private final PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() { new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Tenants default rule chain updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override @Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) { protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
@ -117,8 +144,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
}; };
private PaginatedUpdater<String, Tenant> tenantsDefaultEdgeRuleChainUpdater = private final PaginatedUpdater<String, Tenant> tenantsDefaultEdgeRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() { new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Tenants default edge rule chain updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override @Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) { protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
@ -138,8 +175,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
}; };
private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater = private final PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() { new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Tenants root rule chain updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override @Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) { protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
@ -192,8 +239,18 @@ public class DefaultDataUpdateService implements DataUpdateService {
} }
}; };
private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater = private final PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater =
new PaginatedUpdater<String, Tenant>() { new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Tenants entity views updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override @Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) { protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
@ -261,4 +318,48 @@ public class DefaultDataUpdateService implements DataUpdateService {
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
} }
private final PaginatedUpdater<String, Tenant> tenantsAlarmsCustomerUpdater =
new PaginatedUpdater<>() {
@Override
protected String getName() {
return "Tenants alarms customer updater";
}
@Override
protected boolean forceReportTotal() {
return true;
}
@Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
return tenantService.findTenants(pageLink);
}
@Override
protected void updateEntity(Tenant tenant) {
updateTenantAlarmsCustomer(tenant.getId());
}
};
private void updateTenantAlarmsCustomer(TenantId tenantId) {
AlarmQuery alarmQuery = new AlarmQuery(null, new TimePageLink(100), null, null, false);
PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, alarmQuery);
boolean hasNext = true;
while (hasNext) {
for (Alarm alarm : alarms.getData()) {
if (alarm.getCustomerId() == null && alarm.getOriginator() != null) {
alarm.setCustomerId(entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()));
alarmDao.save(tenantId, alarm);
}
}
if (alarms.hasNext()) {
alarmQuery.setPageLink(alarmQuery.getPageLink().nextPageLink());
alarms = alarmDao.findAlarms(tenantId, alarmQuery);
} else {
hasNext = false;
}
}
}
} }

16
application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java

@ -15,16 +15,20 @@
*/ */
package org.thingsboard.server.service.install.update; package org.thingsboard.server.service.install.update;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@Slf4j
public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> { public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
private static final int DEFAULT_LIMIT = 100; private static final int DEFAULT_LIMIT = 100;
private int updated = 0;
public void updateEntities(I id) { public void updateEntities(I id) {
updated = 0;
PageLink pageLink = new PageLink(DEFAULT_LIMIT); PageLink pageLink = new PageLink(DEFAULT_LIMIT);
boolean hasNext = true; boolean hasNext = true;
while (hasNext) { while (hasNext) {
@ -32,13 +36,25 @@ public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UU
for (D entity : entities.getData()) { for (D entity : entities.getData()) {
updateEntity(entity); updateEntity(entity);
} }
updated += entities.getData().size();
hasNext = entities.hasNext(); hasNext = entities.hasNext();
if (hasNext) { if (hasNext) {
log.info("{}: {} entities updated so far...", getName(), updated);
pageLink = pageLink.nextPageLink(); pageLink = pageLink.nextPageLink();
} else {
if (updated > DEFAULT_LIMIT || forceReportTotal()) {
log.info("{}: {} total entities updated.", getName(), updated);
}
} }
} }
} }
protected boolean forceReportTotal() {
return false;
}
protected abstract String getName();
protected abstract PageData<D> findEntities(I id, PageLink pageLink); protected abstract PageData<D> findEntities(I id, PageLink pageLink);
protected abstract void updateEntity(D entity); protected abstract void updateEntity(D entity);

2
common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityService.java

@ -28,6 +28,8 @@ public interface EntityService {
ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId); ListenableFuture<String> fetchEntityNameAsync(TenantId tenantId, EntityId entityId);
CustomerId fetchEntityCustomerId(TenantId tenantId, EntityId entityId);
void deleteEntityRelations(TenantId tenantId, EntityId entityId); void deleteEntityRelations(TenantId tenantId, EntityId entityId);
long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query); long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query);

6
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -21,9 +21,11 @@ import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTenantId;
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.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -35,9 +37,10 @@ import java.util.List;
@Data @Data
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId { public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, HasCustomerId {
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId;
private String type; private String type;
private EntityId originator; private EntityId originator;
private AlarmSeverity severity; private AlarmSeverity severity;
@ -62,6 +65,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId {
super(alarm.getId()); super(alarm.getId());
this.createdTime = alarm.getCreatedTime(); this.createdTime = alarm.getCreatedTime();
this.tenantId = alarm.getTenantId(); this.tenantId = alarm.getTenantId();
this.customerId = alarm.getCustomerId();
this.type = alarm.getType(); this.type = alarm.getType();
this.originator = alarm.getOriginator(); this.originator = alarm.getOriginator();
this.severity = alarm.getSeverity(); this.severity = alarm.getSeverity();

2
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -110,6 +110,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.getEndTs() == 0L) { if (alarm.getEndTs() == 0L) {
alarm.setEndTs(alarm.getStartTs()); alarm.setEndTs(alarm.getStartTs());
} }
alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()));
if (alarm.getId() == null) { if (alarm.getId() == null) {
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get(); Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
if (existing == null || existing.getStatus().isCleared()) { if (existing == null || existing.getStatus().isCleared()) {
@ -355,6 +356,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
existing.setStatus(alarm.getStatus()); existing.setStatus(alarm.getStatus());
existing.setSeverity(alarm.getSeverity()); existing.setSeverity(alarm.getSeverity());
existing.setDetails(alarm.getDetails()); existing.setDetails(alarm.getDetails());
existing.setCustomerId(alarm.getCustomerId());
existing.setPropagate(existing.isPropagate() || alarm.isPropagate()); existing.setPropagate(existing.isPropagate() || alarm.isPropagate());
List<String> existingPropagateRelationTypes = existing.getPropagateRelationTypes(); List<String> existingPropagateRelationTypes = existing.getPropagateRelationTypes();
List<String> newRelationTypes = alarm.getPropagateRelationTypes(); List<String> newRelationTypes = alarm.getPropagateRelationTypes();

46
dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetId;
@ -54,6 +55,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
/** /**
@ -175,6 +177,50 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe
return entityName; return entityName;
} }
@Override
public CustomerId fetchEntityCustomerId(TenantId tenantId, EntityId entityId) {
log.trace("Executing fetchEntityCustomerId [{}]", entityId);
HasCustomerId hasCustomerId = null;
switch (entityId.getEntityType()) {
case TENANT:
case RULE_CHAIN:
case RULE_NODE:
case DASHBOARD:
case WIDGETS_BUNDLE:
case WIDGET_TYPE:
case TENANT_PROFILE:
case DEVICE_PROFILE:
case API_USAGE_STATE:
case TB_RESOURCE:
case FIRMWARE:
break;
case CUSTOMER:
hasCustomerId = () -> new CustomerId(entityId.getId());
break;
case USER:
hasCustomerId = userService.findUserById(tenantId, new UserId(entityId.getId()));
break;
case ASSET:
hasCustomerId = assetService.findAssetById(tenantId, new AssetId(entityId.getId()));
break;
case DEVICE:
hasCustomerId = deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId()));
break;
case ALARM:
try {
hasCustomerId = alarmService.findAlarmByIdAsync(tenantId, new AlarmId(entityId.getId())).get();
} catch (Exception e) {}
break;
case ENTITY_VIEW:
hasCustomerId = entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId()));
break;
case EDGE:
hasCustomerId = edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId()));
break;
}
return hasCustomerId != null ? hasCustomerId.getCustomerId() : new CustomerId(NULL_UUID);
}
private static void validateEntityCountQuery(EntityCountQuery query) { private static void validateEntityCountQuery(EntityCountQuery query) {
if (query == null) { if (query == null) {
throw new IncorrectParameterException("Query must be specified."); throw new IncorrectParameterException("Query must be specified.");

1
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -258,6 +258,7 @@ public class ModelConstants {
*/ */
public static final String ALARM_COLUMN_FAMILY_NAME = "alarm"; public static final String ALARM_COLUMN_FAMILY_NAME = "alarm";
public static final String ALARM_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ALARM_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ALARM_TYPE_PROPERTY = "type"; public static final String ALARM_TYPE_PROPERTY = "type";
public static final String ALARM_DETAILS_PROPERTY = "details"; public static final String ALARM_DETAILS_PROPERTY = "details";
public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id"; public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";

12
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
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.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseEntity;
@ -44,6 +45,7 @@ import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;
@ -64,6 +66,9 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_TENANT_ID_PROPERTY) @Column(name = ALARM_TENANT_ID_PROPERTY)
private UUID tenantId; private UUID tenantId;
@Column(name = ALARM_CUSTOMER_ID_PROPERTY)
private UUID customerId;
@Column(name = ALARM_ORIGINATOR_ID_PROPERTY) @Column(name = ALARM_ORIGINATOR_ID_PROPERTY)
private UUID originatorId; private UUID originatorId;
@ -115,6 +120,9 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
if (alarm.getTenantId() != null) { if (alarm.getTenantId() != null) {
this.tenantId = alarm.getTenantId().getId(); this.tenantId = alarm.getTenantId().getId();
} }
if (alarm.getCustomerId() != null) {
this.customerId = alarm.getCustomerId().getId();
}
this.type = alarm.getType(); this.type = alarm.getType();
this.originatorId = alarm.getOriginator().getId(); this.originatorId = alarm.getOriginator().getId();
this.originatorType = alarm.getOriginator().getEntityType(); this.originatorType = alarm.getOriginator().getEntityType();
@ -138,6 +146,7 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.setId(alarmEntity.getId()); this.setId(alarmEntity.getId());
this.setCreatedTime(alarmEntity.getCreatedTime()); this.setCreatedTime(alarmEntity.getCreatedTime());
this.tenantId = alarmEntity.getTenantId(); this.tenantId = alarmEntity.getTenantId();
this.customerId = alarmEntity.getCustomerId();
this.type = alarmEntity.getType(); this.type = alarmEntity.getType();
this.originatorId = alarmEntity.getOriginatorId(); this.originatorId = alarmEntity.getOriginatorId();
this.originatorType = alarmEntity.getOriginatorType(); this.originatorType = alarmEntity.getOriginatorType();
@ -159,6 +168,9 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
if (tenantId != null) { if (tenantId != null) {
alarm.setTenantId(new TenantId(tenantId)); alarm.setTenantId(new TenantId(tenantId));
} }
if (customerId != null) {
alarm.setCustomerId(new CustomerId(customerId));
}
alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId)); alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
alarm.setType(type); alarm.setType(type);
alarm.setSeverity(severity); alarm.setSeverity(severity);

5
dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java

@ -19,6 +19,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.dao.util.mapping.JsonStringType; import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Entity; import javax.persistence.Entity;
@ -37,6 +38,10 @@ public final class AlarmEntity extends AbstractAlarmEntity<Alarm> {
super(); super();
} }
public AlarmEntity(AlarmInfo alarmInfo) {
super(alarmInfo);
}
public AlarmEntity(Alarm alarm) { public AlarmEntity(Alarm alarm) {
super(alarm); super(alarm);
} }

136
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -100,12 +100,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Transactional @Transactional
public RuleChain saveRuleChain(RuleChain ruleChain) { public RuleChain saveRuleChain(RuleChain ruleChain) {
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
RuleChain savedRuleChain = ruleChainDao.save(ruleChain.getTenantId(), ruleChain); return ruleChainDao.save(ruleChain.getTenantId(), ruleChain);
if (ruleChain.isRoot() && ruleChain.getId() == null) {
createRelation(ruleChain.getTenantId(), new EntityRelation(savedRuleChain.getTenantId(), savedRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
}
return savedRuleChain;
} }
@Override @Override
@ -114,29 +109,20 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId()); RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
if (!ruleChain.isRoot()) { if (!ruleChain.isRoot()) {
RuleChain previousRootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId()); RuleChain previousRootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId());
try { if (previousRootRuleChain == null) {
if (previousRootRuleChain == null) { setRootAndSave(tenantId, ruleChain);
setRootAndSave(tenantId, ruleChain); return true;
return true; } else if (!previousRootRuleChain.getId().equals(ruleChain.getId())) {
} else if (!previousRootRuleChain.getId().equals(ruleChain.getId())) { previousRootRuleChain.setRoot(false);
deleteRelation(tenantId, new EntityRelation(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(), ruleChainDao.save(tenantId, previousRootRuleChain);
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); setRootAndSave(tenantId, ruleChain);
previousRootRuleChain.setRoot(false); return true;
ruleChainDao.save(tenantId, previousRootRuleChain);
setRootAndSave(tenantId, ruleChain);
return true;
}
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to set root rule chain, ruleChainId: [{}]", ruleChainId);
throw new RuntimeException(e);
} }
} }
return false; return false;
} }
private void setRootAndSave(TenantId tenantId, RuleChain ruleChain) throws ExecutionException, InterruptedException { private void setRootAndSave(TenantId tenantId, RuleChain ruleChain) {
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true); ruleChain.setRoot(true);
ruleChainDao.save(tenantId, ruleChain); ruleChainDao.save(tenantId, ruleChain);
} }
@ -179,41 +165,45 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
toDelete.add(existingNode); toDelete.add(existingNode);
} }
} }
for (RuleNode node : toAddOrUpdate) { if (nodes != null) {
node.setRuleChainId(ruleChain.getId()); for (RuleNode node : toAddOrUpdate) {
RuleNode savedNode = ruleNodeDao.save(tenantId, node); node.setRuleChainId(ruleChain.getId());
createRelation(tenantId, new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), RuleNode savedNode = ruleNodeDao.save(tenantId, node);
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); createRelation(tenantId, new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(),
int index = nodes.indexOf(node); EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
nodes.set(index, savedNode); int index = nodes.indexOf(node);
ruleNodeIndexMap.put(savedNode.getId(), index); nodes.set(index, savedNode);
ruleNodeIndexMap.put(savedNode.getId(), index);
}
} }
for (RuleNode node : toDelete) { for (RuleNode node : toDelete) {
deleteRuleNode(tenantId, node.getId()); deleteRuleNode(tenantId, node.getId());
} }
RuleNodeId firstRuleNodeId = null; RuleNodeId firstRuleNodeId = null;
if (ruleChainMetaData.getFirstNodeIndex() != null) { if (nodes != null) {
firstRuleNodeId = nodes.get(ruleChainMetaData.getFirstNodeIndex()).getId(); if (ruleChainMetaData.getFirstNodeIndex() != null) {
} firstRuleNodeId = nodes.get(ruleChainMetaData.getFirstNodeIndex()).getId();
if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId))
|| (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) {
ruleChain.setFirstRuleNodeId(firstRuleNodeId);
ruleChainDao.save(tenantId, ruleChain);
}
if (ruleChainMetaData.getConnections() != null) {
for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
EntityId from = nodes.get(nodeConnection.getFromIndex()).getId();
EntityId to = nodes.get(nodeConnection.getToIndex()).getId();
String type = nodeConnection.getType();
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE));
} }
} if ((ruleChain.getFirstRuleNodeId() != null && !ruleChain.getFirstRuleNodeId().equals(firstRuleNodeId))
if (ruleChainMetaData.getRuleChainConnections() != null) { || (ruleChain.getFirstRuleNodeId() == null && firstRuleNodeId != null)) {
for (RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) { ruleChain.setFirstRuleNodeId(firstRuleNodeId);
EntityId from = nodes.get(nodeToRuleChainConnection.getFromIndex()).getId(); ruleChainDao.save(tenantId, ruleChain);
EntityId to = nodeToRuleChainConnection.getTargetRuleChainId(); }
String type = nodeToRuleChainConnection.getType(); if (ruleChainMetaData.getConnections() != null) {
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE, nodeToRuleChainConnection.getAdditionalInfo())); for (NodeConnectionInfo nodeConnection : ruleChainMetaData.getConnections()) {
EntityId from = nodes.get(nodeConnection.getFromIndex()).getId();
EntityId to = nodes.get(nodeConnection.getToIndex()).getId();
String type = nodeConnection.getType();
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE));
}
}
if (ruleChainMetaData.getRuleChainConnections() != null) {
for (RuleChainConnectionInfo nodeToRuleChainConnection : ruleChainMetaData.getRuleChainConnections()) {
EntityId from = nodes.get(nodeToRuleChainConnection.getFromIndex()).getId();
EntityId to = nodeToRuleChainConnection.getTargetRuleChainId();
String type = nodeToRuleChainConnection.getType();
createRelation(tenantId, new EntityRelation(from, to, type, RelationTypeGroup.RULE_NODE, nodeToRuleChainConnection.getAdditionalInfo()));
}
} }
} }
@ -307,24 +297,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override @Override
public RuleChain getRootTenantRuleChain(TenantId tenantId) { public RuleChain getRootTenantRuleChain(TenantId tenantId) {
return getRootRuleChainByType(tenantId, RuleChainType.CORE);
}
private RuleChain getRootRuleChainByType(TenantId tenantId, RuleChainType type) {
Validator.validateId(tenantId, "Incorrect tenant id for search request."); Validator.validateId(tenantId, "Incorrect tenant id for search request.");
List<EntityRelation> relations = relationService.findByFrom(tenantId, tenantId, RelationTypeGroup.RULE_CHAIN); return ruleChainDao.findRootRuleChainByTenantIdAndType(tenantId.getId(), RuleChainType.CORE);
if (relations != null && !relations.isEmpty()) {
for (EntityRelation relation : relations) {
RuleChainId ruleChainId = new RuleChainId(relation.getTo().getId());
RuleChain ruleChainById = findRuleChainById(tenantId, ruleChainId);
if (type.equals(ruleChainById.getType())) {
return ruleChainById;
}
}
return null;
} else {
return null;
}
} }
@Override @Override
@ -366,7 +340,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
boolean valid = true; boolean valid = true;
EntityType toType = relation.getTo().getEntityType(); EntityType toType = relation.getTo().getEntityType();
if (toType == EntityType.RULE_NODE || toType == EntityType.RULE_CHAIN) { if (toType == EntityType.RULE_NODE || toType == EntityType.RULE_CHAIN) {
BaseData entity; BaseData<?> entity;
if (relation.getTo().getEntityType() == EntityType.RULE_NODE) { if (relation.getTo().getEntityType() == EntityType.RULE_NODE) {
entity = ruleNodeDao.findById(tenantId, relation.getTo().getId()); entity = ruleNodeDao.findById(tenantId, relation.getTo().getId());
} else { } else {
@ -501,9 +475,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
ObjectNode objNode = (ObjectNode) node; ObjectNode objNode = (ObjectNode) node;
objNode.put("id", tenantId.getId().toString()); objNode.put("id", tenantId.getId().toString());
} else { } else {
Iterator<JsonNode> childIter = node.iterator(); for (JsonNode jsonNode : node) {
while (childIter.hasNext()) { searchTenantIdRecursive(tenantId, jsonNode);
searchTenantIdRecursive(tenantId, childIter.next());
} }
} }
} }
@ -608,7 +581,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override @Override
public RuleChain getEdgeTemplateRootRuleChain(TenantId tenantId) { public RuleChain getEdgeTemplateRootRuleChain(TenantId tenantId) {
return getRootRuleChainByType(tenantId, RuleChainType.EDGE); Validator.validateId(tenantId, "Incorrect tenant id for search request.");
return ruleChainDao.findRootRuleChainByTenantIdAndType(tenantId.getId(), RuleChainType.EDGE);
} }
@Override @Override
@ -618,13 +592,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (previousEdgeTemplateRootRuleChain == null || !previousEdgeTemplateRootRuleChain.getId().equals(ruleChain.getId())) { if (previousEdgeTemplateRootRuleChain == null || !previousEdgeTemplateRootRuleChain.getId().equals(ruleChain.getId())) {
try { try {
if (previousEdgeTemplateRootRuleChain != null) { if (previousEdgeTemplateRootRuleChain != null) {
deleteRelation(tenantId, new EntityRelation(previousEdgeTemplateRootRuleChain.getTenantId(), previousEdgeTemplateRootRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
previousEdgeTemplateRootRuleChain.setRoot(false); previousEdgeTemplateRootRuleChain.setRoot(false);
ruleChainDao.save(tenantId, previousEdgeTemplateRootRuleChain); ruleChainDao.save(tenantId, previousEdgeTemplateRootRuleChain);
} }
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true); ruleChain.setRoot(true);
ruleChainDao.save(tenantId, ruleChain); ruleChainDao.save(tenantId, ruleChain);
return true; return true;
@ -699,8 +669,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
ruleNodeDao.removeById(tenantId, entityId.getId()); ruleNodeDao.removeById(tenantId, entityId.getId());
} }
private DataValidator<RuleChain> ruleChainValidator = private final DataValidator<RuleChain> ruleChainValidator =
new DataValidator<RuleChain>() { new DataValidator<>() {
@Override @Override
protected void validateCreate(TenantId tenantId, RuleChain data) { protected void validateCreate(TenantId tenantId, RuleChain data) {
DefaultTenantProfileConfiguration profileConfiguration = DefaultTenantProfileConfiguration profileConfiguration =
@ -739,8 +709,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
} }
}; };
private PaginatedRemover<TenantId, RuleChain> tenantRuleChainsRemover = private final PaginatedRemover<TenantId, RuleChain> tenantRuleChainsRemover =
new PaginatedRemover<TenantId, RuleChain>() { new PaginatedRemover<>() {
@Override @Override
protected PageData<RuleChain> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { protected PageData<RuleChain> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {

9
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java

@ -50,6 +50,15 @@ public interface RuleChainDao extends Dao<RuleChain>, TenantEntityDao {
*/ */
PageData<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, PageLink pageLink); PageData<RuleChain> findRuleChainsByTenantIdAndType(UUID tenantId, RuleChainType type, PageLink pageLink);
/**
* Find root rule chain by tenantId and type
*
* @param tenantId the tenantId
* @param type the type
* @return the rule chain object
*/
RuleChain findRootRuleChainByTenantIdAndType(UUID tenantId, RuleChainType type);
/** /**
* Find rule chains by tenantId, edgeId and page link. * Find rule chains by tenantId, edgeId and page link.
* *

16
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java

@ -120,13 +120,7 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
Pageable pageable); Pageable pageable);
@Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (" +
"a.originatorId IN (SELECT d.id from DeviceEntity d WHERE d.customerId = :customerId) " +
"OR a.originatorId IN (SELECT asset.id from AssetEntity asset WHERE asset.customerId = :customerId) " +
"OR a.originatorId IN (SELECT u.id from UserEntity u WHERE u.customerId = :customerId) " +
"OR a.originatorId = :customerId" +
") " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +
@ -137,13 +131,7 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
countQuery = "" + countQuery = "" +
"SELECT count(a) " + "SELECT count(a) " +
"FROM AlarmEntity a " + "FROM AlarmEntity a " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (" +
"a.originatorId IN (SELECT d.id from DeviceEntity d WHERE d.customerId = :customerId) " +
"OR a.originatorId IN (SELECT asset.id from AssetEntity asset WHERE asset.customerId = :customerId) " +
"OR a.originatorId IN (SELECT u.id from UserEntity u WHERE u.customerId = :customerId) " +
"OR a.originatorId = :customerId" +
") " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " +

6
dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java

@ -82,6 +82,12 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
DaoUtil.toPageable(pageLink))); DaoUtil.toPageable(pageLink)));
} }
@Override
public RuleChain findRootRuleChainByTenantIdAndType(UUID tenantId, RuleChainType type) {
log.debug("Try to find root rule chain by tenantId [{}] and type [{}]", tenantId, type);
return DaoUtil.getData(ruleChainRepository.findByTenantIdAndTypeAndRootIsTrue(tenantId, type));
}
@Override @Override
public PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { public PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) {
log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);

3
dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java

@ -50,5 +50,8 @@ public interface RuleChainRepository extends PagingAndSortingRepository<RuleChai
@Param("edgeId") UUID edgeId, @Param("edgeId") UUID edgeId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
RuleChainEntity findByTenantIdAndTypeAndRootIsTrue(UUID tenantId, RuleChainType ruleChainType);
Long countByTenantId(UUID tenantId); Long countByTenantId(UUID tenantId);
} }

1
dao/src/main/resources/sql/schema-entities-hsql.sql

@ -35,6 +35,7 @@ CREATE TABLE IF NOT EXISTS alarm (
start_ts bigint, start_ts bigint,
status varchar(255), status varchar(255),
tenant_id uuid, tenant_id uuid,
customer_id uuid,
propagate_relation_types varchar, propagate_relation_types varchar,
type varchar(255) type varchar(255)
); );

1
dao/src/main/resources/sql/schema-entities.sql

@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS alarm (
start_ts bigint, start_ts bigint,
status varchar(255), status varchar(255),
tenant_id uuid, tenant_id uuid,
customer_id uuid,
propagate_relation_types varchar, propagate_relation_types varchar,
type varchar(255) type varchar(255)
); );

6
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

@ -188,12 +188,12 @@
</mat-drawer-content> </mat-drawer-content>
</mat-drawer-container> </mat-drawer-container>
<section fxLayout="row" class="layout-wrap tb-footer-buttons" fxLayoutAlign="start end"> <section fxLayout="row" class="layout-wrap tb-footer-buttons" fxLayoutAlign="start end">
<tb-footer-fab-buttons *ngIf="!embedded" <tb-footer-fab-buttons *ngIf="!embedded && !isMobileApp"
[fxShow]="!isAddingWidget && isEdit && !widgetEditMode" [fxShow]="!isAddingWidget && isEdit && !widgetEditMode"
relative relative
[footerFabButtons]="addWidgetFabButtons"> [footerFabButtons]="addWidgetFabButtons">
</tb-footer-fab-buttons> </tb-footer-fab-buttons>
<button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen && !embedded" <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen && !embedded && !isMobileApp"
mat-fab color="accent" class="tb-btn-footer" mat-fab color="accent" class="tb-btn-footer"
[ngClass]="{'tb-hide': !isEdit || isAddingWidget}" [ngClass]="{'tb-hide': !isEdit || isAddingWidget}"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"
@ -202,7 +202,7 @@
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>done</mat-icon> <mat-icon>done</mat-icon>
</button> </button>
<button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen && !embedded" <button *ngIf="(isTenantAdmin() || isSystemAdmin()) && !forceFullscreen && !embedded && !isMobileApp"
mat-fab color="accent" class="tb-btn-footer" mat-fab color="accent" class="tb-btn-footer"
[ngClass]="{'tb-hide': isAddingWidget || (isLoading$ | async)}" [ngClass]="{'tb-hide': isAddingWidget || (isLoading$ | async)}"
[disabled]="isLoading$ | async" [disabled]="isLoading$ | async"

Loading…
Cancel
Save