diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 8a1f1cb5a8..005c740571 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -77,7 +77,7 @@ public interface DeviceService extends EntityDaoService { PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type, PageLink pageLink); - Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType); + long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType); ListenableFuture> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List deviceIds); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java index 1b62f4c36a..93a170ef94 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java @@ -22,4 +22,8 @@ public interface HasOtaPackage { OtaPackageId getFirmwareId(); OtaPackageId getSoftwareId(); + + void setFirmwareId(OtaPackageId otaPackageId); + + void setSoftwareId(OtaPackageId otaPackageId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 2c80354923..e6ba0e5e72 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -407,7 +407,7 @@ public class DeviceServiceImpl extends CachedVersionedEntityService INCORRECT_TENANT_ID + id); validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 289da44626..c55210b606 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -83,33 +83,27 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + - "AND d.firmwareId = null " + - "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + - "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") + "AND d.firmwareId IS NULL") Page findByTenantIdAndTypeAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, - @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + - "AND d.softwareId = null " + - "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + - "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") + "AND d.softwareId IS NULL") Page findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, - @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + - "AND d.firmwareId = null") + "AND d.firmwareId IS NULL") Long countByTenantIdAndDeviceProfileIdAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId); @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + - "AND d.softwareId = null") + "AND d.softwareId IS NULL") Long countByTenantIdAndDeviceProfileIdAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index f6ff3c8915..48bb998016 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -179,10 +179,9 @@ public class JpaDeviceDao extends JpaAbstractDao implement OtaPackageType type, PageLink pageLink) { Pageable pageable = DaoUtil.toPageable(pageLink); - String searchText = pageLink.getTextSearch(); Page page = OtaPackageUtil.getByOtaPackageType( - () -> deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), - () -> deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), + () -> deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(tenantId, deviceProfileId, pageable), + () -> deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(tenantId, deviceProfileId, pageable), type ); return DaoUtil.toPageData(page); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java index a54399e85d..9fe3f85463 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java @@ -33,13 +33,18 @@ import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceInfoFilter; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.HasOtaPackage; import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -63,6 +68,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @DaoSqlTest @@ -202,6 +208,125 @@ public class DeviceServiceTest extends AbstractServiceTest { deleteDevice(anotherTenantId, anotherDevice); } + @Test + public void testCountDevicesWithoutFirmware() { + testCountDevicesWithoutOta(FIRMWARE); + } + + @Test + public void testCountDevicesWithoutSoftware() { + testCountDevicesWithoutOta(SOFTWARE); + } + + public void testCountDevicesWithoutOta(OtaPackageType type) { + var defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); + var deviceProfileId = defaultDeviceProfile.getId(); + Assert.assertEquals(0, deviceService.countByTenantId(tenantId)); + Assert.assertEquals(0, deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId, deviceProfileId, type)); + + int maxDevices = 8; + List devices = new ArrayList<>(maxDevices); + + for (int i = 1; i <= maxDevices; i++) { + devices.add(this.saveDevice(tenantId, "My device " + i)); + Assert.assertEquals(i, deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId, deviceProfileId, type)); + } + + Assert.assertEquals(maxDevices, deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId, deviceProfileId, type)); + + var otaPackageId = createOta(deviceProfileId, type); + + int devicesWithOta = maxDevices / 2; + + for (int i = 0; i < devicesWithOta; i++) { + var device = devices.get(i); + setOtaPackageId(device, type, otaPackageId); + deviceService.saveDevice(device); + } + + Assert.assertEquals(maxDevices - devicesWithOta, deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId, deviceProfileId, type)); + + devices.forEach(device -> deleteDevice(tenantId, device)); + } + + @Test + public void testFindDevicesWithoutFirmware() { + testFindDevicesWithoutOta(FIRMWARE); + } + + @Test + public void testFindDevicesWithoutSoftware() { + testFindDevicesWithoutOta(SOFTWARE); + } + + public void testFindDevicesWithoutOta(OtaPackageType type) { + var defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); + var deviceProfileId = defaultDeviceProfile.getId(); + + PageLink pageLink = new PageLink(100); + + Assert.assertEquals(0, deviceService.countByTenantId(tenantId)); + Assert.assertEquals(0, deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfileId, type, pageLink).getData().size()); + + int maxDevices = 8; + List devices = new ArrayList<>(maxDevices); + + for (int i = 1; i <= maxDevices; i++) { + devices.add(this.saveDevice(tenantId, "My device " + i)); + } + + var foundDevices = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfileId, type, pageLink).getData(); + Assert.assertEquals(maxDevices, foundDevices.size()); + + devices.sort(idComparator); + foundDevices.sort(idComparator); + + Assert.assertEquals(devices, foundDevices); + + var otaPackageId = createOta(deviceProfileId, type); + + int devicesWithOta = maxDevices / 2; + + for (int i = 0; i < devicesWithOta; i++) { + var device = devices.get(i); + setOtaPackageId(device, type, otaPackageId); + deviceService.saveDevice(device); + } + + foundDevices = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfileId, type, pageLink).getData(); + + Assert.assertEquals(maxDevices - devicesWithOta, foundDevices.size()); + + foundDevices.sort(idComparator); + + for (int i = 0; i < foundDevices.size(); i++) { + Assert.assertEquals(devices.get(i + devicesWithOta), foundDevices.get(i)); + } + + devices.forEach(device -> deleteDevice(tenantId, device)); + } + + private void setOtaPackageId(T obj, OtaPackageType type, OtaPackageId otaPackageId) { + switch (type) { + case FIRMWARE -> obj.setFirmwareId(otaPackageId); + case SOFTWARE -> obj.setSoftwareId(otaPackageId); + } + } + + private OtaPackageId createOta(DeviceProfileId deviceProfileId, OtaPackageType type) { + OtaPackageInfo ota = new OtaPackageInfo(); + ota.setTenantId(tenantId); + ota.setDeviceProfileId(deviceProfileId); + ota.setType(type); + ota.setTitle("Test_" + type); + ota.setVersion("v1.0"); + ota.setUrl("http://ota.test.org"); + ota.setDataSize(0L); + OtaPackageInfo savedOta = otaPackageService.saveOtaPackageInfo(ota, true); + Assert.assertNotNull(savedOta); + return savedOta.getId(); + } + void deleteDevice(TenantId tenantId, Device device) { deviceService.deleteDevice(tenantId, device.getId()); }