diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index af3fd418a8..4bb63f3b9f 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -78,7 +78,11 @@ CREATE TABLE IF NOT EXISTS firmware ( CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); +ALTER TABLE dashboard + ADD COLUMN IF NOT EXISTS image varchar(1000000); + ALTER TABLE device_profile + ADD COLUMN IF NOT EXISTS image varchar(1000000), ADD COLUMN IF NOT EXISTS firmware_id uuid, ADD COLUMN IF NOT EXISTS software_id uuid; diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 9453bd46b7..6aec756f61 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -196,6 +196,41 @@ public class AlarmController extends BaseController { } } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/alarms", method = RequestMethod.GET) + @ResponseBody + public PageData getAllAlarms( + @RequestParam(required = false) String searchStatus, + @RequestParam(required = false) String status, + @RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder, + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false) Boolean fetchOriginator + ) throws ThingsboardException { + accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, Operation.READ); + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); + AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); + if (alarmSearchStatus != null && alarmStatus != null) { + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " + + "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + + try { + if (getCurrentUser().isCustomerUser()) { + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); + } else { + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get()); + } + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index c2ff292667..82700ff064 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -127,6 +127,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return alarmService.findAlarms(tenantId, query); } + @Override + public ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { + return alarmService.findCustomerAlarms(tenantId, customerId, query); + } + @Override public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus) { return alarmService.findHighestAlarmSeverity(tenantId, entityId, alarmSearchStatus, alarmStatus); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java index 310fb40215..95a878f9db 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java @@ -313,7 +313,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator); List deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), - deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); + deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 5badd872df..2bca57b1b0 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -53,6 +53,8 @@ public interface AlarmService { ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query); + ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); + AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 854c5ff64b..cbcd213377 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -30,6 +30,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa private TenantId tenantId; @NoXss private String title; + private String image; @Valid private Set assignedCustomers; @@ -45,6 +46,7 @@ public class DashboardInfo extends SearchTextBased implements HasNa super(dashboardInfo); this.tenantId = dashboardInfo.getTenantId(); this.title = dashboardInfo.getTitle(); + this.image = dashboardInfo.getImage(); this.assignedCustomers = dashboardInfo.getAssignedCustomers(); } @@ -64,6 +66,14 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.title = title; } + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + public Set getAssignedCustomers() { return assignedCustomers; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index daf7d77af0..8104f6bf80 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -43,6 +43,7 @@ public class DeviceProfile extends SearchTextBased implements H private String name; @NoXss private String description; + private String image; private boolean isDefault; private DeviceProfileType type; private DeviceTransportType transportType; @@ -74,6 +75,7 @@ public class DeviceProfile extends SearchTextBased implements H this.tenantId = deviceProfile.getTenantId(); this.name = deviceProfile.getName(); this.description = deviceProfile.getDescription(); + this.image = deviceProfile.getImage(); this.isDefault = deviceProfile.isDefault(); this.defaultRuleChainId = deviceProfile.getDefaultRuleChainId(); this.defaultQueueName = deviceProfile.getDefaultQueueName(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java index db83c58d13..c134934d66 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java @@ -30,21 +30,25 @@ import java.util.UUID; @ToString(callSuper = true) public class DeviceProfileInfo extends EntityInfo { + private final String image; private final DeviceProfileType type; private final DeviceTransportType transportType; @JsonCreator public DeviceProfileInfo(@JsonProperty("id") EntityId id, @JsonProperty("name") String name, + @JsonProperty("image") String image, @JsonProperty("type") DeviceProfileType type, @JsonProperty("transportType") DeviceTransportType transportType) { super(id, name); + this.image = image; this.type = type; this.transportType = transportType; } - public DeviceProfileInfo(UUID uuid, String name, DeviceProfileType type, DeviceTransportType transportType) { + public DeviceProfileInfo(UUID uuid, String name, String image, DeviceProfileType type, DeviceTransportType transportType) { super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name); + this.image = image; this.type = type; this.transportType = transportType; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index ebbfae0f05..eb873db679 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -48,6 +48,8 @@ public interface AlarmDao extends Dao { PageData findAlarms(TenantId tenantId, AlarmQuery query); + PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); + PageData findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection orderedEntityIds); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 9dff668f2e..472cbb588f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -292,25 +292,38 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ public ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query) { PageData alarms = alarmDao.findAlarms(tenantId, query); if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { - List> alarmFutures = new ArrayList<>(alarms.getData().size()); - for (AlarmInfo alarmInfo : alarms.getData()) { - alarmFutures.add(Futures.transform( - entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> { - if (originatorName == null) { - originatorName = "Deleted"; - } - alarmInfo.setOriginatorName(originatorName); - return alarmInfo; - }, MoreExecutors.directExecutor() - )); - } - return Futures.transform(Futures.successfulAsList(alarmFutures), - alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(), - alarms.hasNext()), MoreExecutors.directExecutor()); + return fetchAlarmsOriginators(tenantId, alarms); } return Futures.immediateFuture(alarms); } + @Override + public ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { + PageData alarms = alarmDao.findCustomerAlarms(tenantId, customerId, query); + if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { + return fetchAlarmsOriginators(tenantId, alarms); + } + return Futures.immediateFuture(alarms); + } + + private ListenableFuture> fetchAlarmsOriginators(TenantId tenantId, PageData alarms) { + List> alarmFutures = new ArrayList<>(alarms.getData().size()); + for (AlarmInfo alarmInfo : alarms.getData()) { + alarmFutures.add(Futures.transform( + entityService.fetchEntityNameAsync(tenantId, alarmInfo.getOriginator()), originatorName -> { + if (originatorName == null) { + originatorName = "Deleted"; + } + alarmInfo.setOriginatorName(originatorName); + return alarmInfo; + }, MoreExecutors.directExecutor() + )); + } + return Futures.transform(Futures.successfulAsList(alarmFutures), + alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(), + alarms.hasNext()), MoreExecutors.directExecutor()); + } + @Override public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index bc0afe630d..5b4ea874ee 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -170,6 +170,7 @@ public class ModelConstants { public static final String DEVICE_PROFILE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String DEVICE_PROFILE_NAME_PROPERTY = "name"; public static final String DEVICE_PROFILE_TYPE_PROPERTY = "type"; + public static final String DEVICE_PROFILE_IMAGE_PROPERTY = "image"; public static final String DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY = "transport_type"; public static final String DEVICE_PROFILE_PROVISION_TYPE_PROPERTY = "provision_type"; public static final String DEVICE_PROFILE_PROFILE_DATA_PROPERTY = "profile_data"; @@ -333,6 +334,7 @@ public class ModelConstants { public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; + public static final String DASHBOARD_IMAGE_PROPERTY = "image"; public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index e0780a5fc9..8e499858bc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java @@ -58,7 +58,10 @@ public final class DashboardEntity extends BaseSqlEntity implements S @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) private String title; - + + @Column(name = ModelConstants.DASHBOARD_IMAGE_PROPERTY) + private String image; + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) private String searchText; @@ -82,6 +85,7 @@ public final class DashboardEntity extends BaseSqlEntity implements S this.tenantId = dashboard.getTenantId().getId(); } this.title = dashboard.getTitle(); + this.image = dashboard.getImage(); if (dashboard.getAssignedCustomers() != null) { try { this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); @@ -110,6 +114,7 @@ public final class DashboardEntity extends BaseSqlEntity implements S dashboard.setTenantId(new TenantId(tenantId)); } dashboard.setTitle(title); + dashboard.setImage(image); if (!StringUtils.isEmpty(assignedCustomers)) { try { dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index 47c7016aba..b3d7738843 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java @@ -54,6 +54,9 @@ public class DashboardInfoEntity extends BaseSqlEntity implements @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) private String title; + @Column(name = ModelConstants.DASHBOARD_IMAGE_PROPERTY) + private String image; + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) private String searchText; @@ -73,6 +76,7 @@ public class DashboardInfoEntity extends BaseSqlEntity implements this.tenantId = dashboardInfo.getTenantId().getId(); } this.title = dashboardInfo.getTitle(); + this.image = dashboardInfo.getImage(); if (dashboardInfo.getAssignedCustomers() != null) { try { this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); @@ -104,6 +108,7 @@ public class DashboardInfoEntity extends BaseSqlEntity implements dashboardInfo.setTenantId(new TenantId(tenantId)); } dashboardInfo.setTitle(title); + dashboardInfo.setImage(image); if (!StringUtils.isEmpty(assignedCustomers)) { try { dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index e4c32a44f0..f66153abbc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -60,6 +60,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl @Column(name = ModelConstants.DEVICE_PROFILE_TYPE_PROPERTY) private DeviceProfileType type; + @Column(name = ModelConstants.DEVICE_PROFILE_IMAGE_PROPERTY) + private String image; + @Enumerated(EnumType.STRING) @Column(name = ModelConstants.DEVICE_PROFILE_TRANSPORT_TYPE_PROPERTY) private DeviceTransportType transportType; @@ -110,6 +113,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl this.setCreatedTime(deviceProfile.getCreatedTime()); this.name = deviceProfile.getName(); this.type = deviceProfile.getType(); + this.image = deviceProfile.getImage(); this.transportType = deviceProfile.getTransportType(); this.provisionType = deviceProfile.getProvisionType(); this.description = deviceProfile.getDescription(); @@ -151,6 +155,7 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl } deviceProfile.setName(name); deviceProfile.setType(type); + deviceProfile.setImage(image); deviceProfile.setTransportType(transportType); deviceProfile.setProvisionType(provisionType); deviceProfile.setDescription(description); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 446125e4fb..028eda1583 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -94,6 +94,70 @@ public interface AlarmRepository extends CrudRepository { @Param("searchText") String searchText, Pageable pageable); + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " + + "WHERE a.tenantId = :tenantId " + + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ", + countQuery = "" + + "SELECT count(a) " + + "FROM AlarmEntity a " + + "WHERE a.tenantId = :tenantId " + + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ") + Page findAllAlarms(@Param("tenantId") UUID tenantId, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime, + @Param("alarmStatuses") Set alarmStatuses, + @Param("searchText") String searchText, + Pageable pageable); + + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " + + "WHERE a.tenantId = :tenantId " + + "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 (:endTime IS NULL OR a.createdTime <= :endTime) " + + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) " + , + countQuery = "" + + "SELECT count(a) " + + "FROM AlarmEntity a " + + "WHERE a.tenantId = :tenantId " + + "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 (:endTime IS NULL OR a.createdTime <= :endTime) " + + "AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%')) " + + " OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%'))) ") + Page findCustomerAlarms(@Param("tenantId") UUID tenantId, + @Param("customerId") UUID customerId, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime, + @Param("alarmStatuses") Set alarmStatuses, + @Param("searchText") String searchText, + Pageable pageable); + @Query(value = "SELECT a.severity FROM AlarmEntity a " + "LEFT JOIN RelationEntity re ON a.id = re.toId " + "AND re.relationTypeGroup = 'ALARM' " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index bfca088c69..cf222413b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -103,11 +103,46 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } else if (query.getStatus() != null) { statusSet = Collections.singleton(query.getStatus()); } + if (affectedEntity != null) { + return DaoUtil.toPageData( + alarmRepository.findAlarms( + tenantId.getId(), + affectedEntity.getId(), + affectedEntity.getEntityType().name(), + query.getPageLink().getStartTime(), + query.getPageLink().getEndTime(), + statusSet, + Objects.toString(query.getPageLink().getTextSearch(), ""), + DaoUtil.toPageable(query.getPageLink()) + ) + ); + } else { + return DaoUtil.toPageData( + alarmRepository.findAllAlarms( + tenantId.getId(), + query.getPageLink().getStartTime(), + query.getPageLink().getEndTime(), + statusSet, + Objects.toString(query.getPageLink().getTextSearch(), ""), + DaoUtil.toPageable(query.getPageLink()) + ) + ); + } + } + + @Override + public PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { + log.trace("Try to find customer alarms by status [{}] and pageLink [{}]", query.getStatus(), query.getPageLink()); + Set statusSet = null; + if (query.getSearchStatus() != null) { + statusSet = query.getSearchStatus().getStatuses(); + } else if (query.getStatus() != null) { + statusSet = Collections.singleton(query.getStatus()); + } return DaoUtil.toPageData( - alarmRepository.findAlarms( + alarmRepository.findCustomerAlarms( tenantId.getId(), - affectedEntity.getId(), - affectedEntity.getEntityType().name(), + customerId.getId(), query.getPageLink().getStartTime(), query.getPageLink().getEndTime(), statusSet, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java index c4d6db691b..33d1c3ece1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java @@ -28,7 +28,7 @@ import java.util.UUID; public interface DeviceProfileRepository extends PagingAndSortingRepository { - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " + "FROM DeviceProfileEntity d " + "WHERE d.id = :deviceProfileId") DeviceProfileInfo findDeviceProfileInfoById(@Param("deviceProfileId") UUID deviceProfileId); @@ -39,14 +39,14 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository findDeviceProfileInfos(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); - @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " + + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.type, d.transportType) " + "FROM DeviceProfileEntity d WHERE " + "d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") Page findDeviceProfileInfos(@Param("tenantId") UUID tenantId, @@ -58,7 +58,7 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository deviceProfileInfos = deviceProfiles.stream() .map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), - deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); + deviceProfile.getName(), deviceProfile.getImage(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index d6a0a3e576..7d3157588b 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -57,6 +57,8 @@ public interface RuleEngineAlarmService { ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query); + ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); + AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus); PageData findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataQuery query, Collection orderedEntityIds); diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html index 23219f1074..fa9b03bcb3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -60,6 +60,11 @@ {{ 'device-profile.type-required' | translate }} + + device-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts index 9ad7993857..1de74c9de5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts @@ -106,6 +106,7 @@ export class AddDeviceProfileDialogComponent extends { name: [data.deviceProfileName, [Validators.required]], type: [DeviceProfileType.DEFAULT, [Validators.required]], + image: [null, []], defaultRuleChainId: [null, []], defaultQueueName: ['', []], description: ['', []] @@ -183,6 +184,7 @@ export class AddDeviceProfileDialogComponent extends const deviceProfile: DeviceProfile = { name: this.deviceProfileDetailsFormGroup.get('name').value, type: this.deviceProfileDetailsFormGroup.get('type').value, + image: this.deviceProfileDetailsFormGroup.get('image').value, transportType: this.transportConfigFormGroup.get('transportType').value, provisionType: deviceProvisionConfiguration.type, provisionDeviceKey, diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 5c21d65adc..cff6177955 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -86,6 +86,11 @@ {{ 'device-profile.type-required' | translate }} + + device-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index d8505a0a3b..1e8699210d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -103,6 +103,7 @@ export class DeviceProfileComponent extends EntityComponent { { name: [entity ? entity.name : '', [Validators.required]], type: [entity ? entity.type : null, [Validators.required]], + image: [entity ? entity.image : null], transportType: [entity ? entity.transportType : null, [Validators.required]], profileData: this.fb.group({ configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required], @@ -178,6 +179,7 @@ export class DeviceProfileComponent extends EntityComponent { }; this.entityForm.patchValue({name: entity.name}); this.entityForm.patchValue({type: entity.type}, {emitEvent: false}); + this.entityForm.patchValue({image: entity.image}, {emitEvent: false}); this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false}); this.entityForm.patchValue({provisionType: entity.provisionType}, {emitEvent: false}); this.entityForm.patchValue({provisionDeviceKey: entity.provisionDeviceKey}, {emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html index 1fb8978f22..ca7512d7ec 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html @@ -105,6 +105,11 @@ {{ 'dashboard.title-required' | translate }} + +
dashboard.description diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts index 37030c57f1..70914e069d 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts @@ -80,6 +80,7 @@ export class DashboardFormComponent extends EntityComponent { return this.fb.group( { title: [entity ? entity.title : '', [Validators.required]], + image: [entity ? entity.image : null], configuration: this.fb.group( { description: [entity && entity.configuration ? entity.configuration.description : ''], @@ -92,6 +93,7 @@ export class DashboardFormComponent extends EntityComponent { updateForm(entity: Dashboard) { this.updateFields(entity); this.entityForm.patchValue({title: entity.title}); + this.entityForm.patchValue({image: entity.image}); this.entityForm.patchValue({configuration: {description: entity.configuration ? entity.configuration.description : ''}}); } diff --git a/ui-ngx/src/app/shared/components/image-input.component.html b/ui-ngx/src/app/shared/components/image-input.component.html index 4b7e4fe1d4..2fe8cc4102 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.html +++ b/ui-ngx/src/app/shared/components/image-input.component.html @@ -21,10 +21,10 @@ [flowConfig]="{singleFile: true, allowDuplicateUploads: true}">
-
dashboard.no-image
+
{{ (disabled ? 'dashboard.empty-image' : 'dashboard.no-image') | translate }}
-
+
-
@@ -42,5 +42,5 @@
-
dashboard.maximum-upload-file-size
+
dashboard.maximum-upload-file-size
diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index 6a90101923..98a19d1b13 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -26,6 +26,7 @@ import { Filters } from '@shared/models/query/query.models'; export interface DashboardInfo extends BaseData { tenantId?: TenantId; title?: string; + image?: string; assignedCustomers?: Array; } diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 683d9ddcc1..81fdb7a8fd 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -492,6 +492,7 @@ export interface DeviceProfile extends BaseData { description?: string; default?: boolean; type: DeviceProfileType; + image?: string; transportType: DeviceTransportType; provisionType: DeviceProvisionType; provisionDeviceKey?: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f664dc27a7..adfd8434a2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -666,6 +666,7 @@ "no-widgets": "No widgets configured", "add-widget": "Add new widget", "title": "Title", + "image": "Dashboard image", "select-widget-title": "Select widget", "select-widget-value": "{{title}}: select widget", "select-widget-subtitle": "List of available widget types", @@ -712,6 +713,7 @@ "background-image": "Background image", "background-size-mode": "Background size mode", "no-image": "No image selected", + "empty-image": "No image", "drop-image": "Drop an image or click to select a file to upload.", "maximum-upload-file-size": "Maximum upload file size: {{ size }}", "cannot-upload-file": "Cannot upload file", @@ -1028,6 +1030,7 @@ "type": "Profile type", "type-required": "Profile type is required.", "type-default": "Default", + "image": "Device profile image", "transport-type": "Transport type", "transport-type-required": "Transport type is required.", "transport-type-default": "Default",