diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index fb32ebd30b..c1248a1fbf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -300,9 +300,15 @@ public class NotificationController extends BaseController { request.setOriginatorEntityId(user.getId()); List targets = request.getTargets().stream() .map(NotificationTargetId::new) - .map(targetId -> notificationTargetService.findNotificationTargetById(user.getTenantId(), targetId)) + .map(targetId -> { + NotificationTarget target = notificationTargetService.findNotificationTargetById(user.getTenantId(), targetId); + if (target == null) { + throw new IllegalArgumentException("Notification target for id " + targetId + " not found"); + } + return target; + }) .sorted(Comparator.comparing(target -> target.getConfiguration().getType())) - .collect(Collectors.toList()); + .toList(); NotificationRequestPreview preview = new NotificationRequestPreview(); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index f20b666b28..84d3b6c378 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -73,6 +73,7 @@ import org.thingsboard.server.service.telemetry.AbstractSubscriptionService; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -115,12 +116,23 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple } else { notificationTemplate = request.getTemplate(); } - if (notificationTemplate == null) throw new IllegalArgumentException("Template is missing"); + if (notificationTemplate == null) { + throw new IllegalArgumentException("Template is missing"); + } Set deliveryMethods = new HashSet<>(); - List targets = request.getTargets().stream().map(NotificationTargetId::new) - .map(id -> notificationTargetService.findNotificationTargetById(tenantId, id)) - .collect(Collectors.toList()); + List targets = new ArrayList<>(); + for (UUID targetId : request.getTargets()) { + NotificationTarget target = notificationTargetService.findNotificationTargetById(tenantId, new NotificationTargetId(targetId)); + if (target != null) { + targets.add(target); + } else { + log.debug("Unknown notification target {} in request {}", targetId, request); + } + } + if (targets.isEmpty()) { + throw new IllegalArgumentException("No recipients chosen"); + } NotificationRuleId ruleId = request.getRuleId(); notificationTemplate.getConfiguration().getDeliveryMethodsTemplates().forEach((deliveryMethod, template) -> { diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java index 89e6754d19..765c82a8db 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java @@ -53,7 +53,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key); + TbCacheValueWrapper cacheValueWrapper = get(key, true); if (cacheValueWrapper != null) { return cacheValueWrapper.get(); } @@ -92,7 +92,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key); + TbCacheValueWrapper cacheValueWrapper = get(key, true); if (cacheValueWrapper != null) { var cacheValue = cacheValueWrapper.get(); return cacheValue == null ? null : cacheValueToResult.apply(cacheValue); 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/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 751945a360..aaa4c22289 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -467,6 +467,9 @@ public class GitRepository { if (RepositoryAuthMethod.USERNAME_PASSWORD.equals(settings.getAuthMethod())) { credentialsProvider = newCredentialsProvider(settings.getUsername(), settings.getPassword()); } else if (RepositoryAuthMethod.PRIVATE_KEY.equals(settings.getAuthMethod())) { + if (StringUtils.startsWith(settings.getRepositoryUri(), "https://")) { + throw new IllegalArgumentException("Invalid URI format for private key authentication"); + } sshSessionFactory = newSshdSessionFactory(settings.getPrivateKey(), settings.getPrivateKeyPassword(), directory); } return new AuthHandler(credentialsProvider, sshSessionFactory); 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/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index c5e6bc4968..7b3bbe2db2 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 @@ -25,6 +25,7 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.ToString; import org.hibernate.annotations.JdbcType; import org.hibernate.dialect.PostgreSQLJsonPGObjectJsonbType; import org.thingsboard.common.util.JacksonUtil; @@ -48,6 +49,7 @@ import java.util.UUID; @EqualsAndHashCode(callSuper = true) @Entity @Table(name = ModelConstants.DEVICE_PROFILE_TABLE_NAME) +@ToString(callSuper = true) public final class DeviceProfileEntity extends BaseVersionedEntity { @Column(name = ModelConstants.DEVICE_PROFILE_TENANT_ID_PROPERTY) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 8002730686..801898386e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -58,6 +58,10 @@ public abstract class JpaAbstractDao, D> @Override @Transactional public D save(TenantId tenantId, D domain) { + return save(tenantId, domain, false); + } + + private D save(TenantId tenantId, D domain, boolean flush) { E entity; try { entity = getEntityClass().getConstructor(domain.getClass()).newInstance(domain); @@ -73,14 +77,15 @@ public abstract class JpaAbstractDao, D> entity.setCreatedTime(Uuids.unixTimestamp(uuid)); } try { - entity = doSave(entity, isNew); + entity = doSave(entity, isNew, flush); } catch (OptimisticLockException e) { throw new EntityVersionMismatchException((getEntityType() != null ? getEntityType().getNormalName() : "Entity") + " was already changed by someone else", e); } return DaoUtil.getData(entity); } - protected E doSave(E entity, boolean isNew) { + protected E doSave(E entity, boolean isNew, boolean flush) { + boolean flushed = false; EntityManager entityManager = getEntityManager(); if (isNew) { if (entity instanceof HasVersion versionedEntity) { @@ -94,24 +99,32 @@ public abstract class JpaAbstractDao, D> if (existingEntity != null) { versionedEntity.setVersion(existingEntity.getVersion()); // manually resetting the version to latest to allow force overwrite of the entity } else { - return doSave(entity, true); + return doSave(entity, true, flush); } } entity = entityManager.merge(entity); + /* + * flushing so that the query is executed right away and the version is incremented, + * then removing the entity from the persistence context so that it is not affected + * by next flushes (e.g. when a transaction is committed) to avoid double version increment + * */ entityManager.flush(); + entityManager.detach(entity); + flushed = true; } else { entity = entityManager.merge(entity); } } + if (flush && !flushed) { + entityManager.flush(); + } return entity; } @Override @Transactional public D saveAndFlush(TenantId tenantId, D domain) { - D d = save(tenantId, domain); - getRepository().flush(); - return d; + return save(tenantId, domain, true); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java index 6afb8b4ede..a74f32d8c4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -22,9 +22,9 @@ import org.thingsboard.server.dao.util.SqlDao; public abstract class JpaPartitionedAbstractDao, D> extends JpaAbstractDao { @Override - protected E doSave(E entity, boolean isNew) { + protected E doSave(E entity, boolean isNew, boolean flush) { createPartition(entity); - return super.doSave(entity, isNew); + return super.doSave(entity, isNew, flush); } public abstract void createPartition(E entity); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java index d6025fc07b..6ed74a5aa9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java @@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.AssetProfile; @@ -58,14 +57,6 @@ public class JpaAssetProfileDao extends JpaAbstractDao findAssetProfiles(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData( 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/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 0f597756eb..4e1594eeff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; @@ -62,14 +61,6 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findDeviceProfiles(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData( 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()); } diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index d11b28a97f..28ebbfaf79 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -76,7 +76,7 @@ export enum MenuId { mobile_app_settings = 'mobile_app_settings', security_settings = 'security_settings', security_settings_general = 'security_settings_general', - two_fa = '2fa', + two_fa = 'two_fa', oauth2 = 'oauth2', audit_log = 'audit_log', alarms = 'alarms', diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 5af1833794..0d379b1623 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -1227,7 +1227,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC }; this.init(dashboardPageInitData); } else { - this.dashboard = dashboard; + this.dashboard.version = dashboard.version; this.setEditMode(false, false); } });