From 17705dec080c1c378bf4cbd714cdc91f5b2ba736 Mon Sep 17 00:00:00 2001 From: AnastasiiaKomar Date: Fri, 12 Aug 2022 16:36:10 +0300 Subject: [PATCH] improved downloading ota in deviceTransportAPi --- .../ota/DefaultTbOtaPackageService.java | 12 ++- .../service/ota/TbMultipartFileImp.java | 42 +++++++++ .../transport/DefaultTransportApiService.java | 20 +++-- .../sync/ie/BaseExportImportServiceTest.java | 44 ++++++++- .../cache/ota/files/TemporaryFileCleaner.java | 16 ++-- .../server/dao/ota/OtaPackageService.java | 3 +- .../server/dao/ota/TbMultipartFile.java | 15 ++++ .../transport/http/DeviceApiController.java | 29 ++++-- .../server/dao/ota/BaseOtaPackageService.java | 23 ++--- .../AbstractHasOtaPackageValidator.java | 19 ++-- .../service/BaseDeviceProfileServiceTest.java | 43 ++++++++- .../dao/service/BaseDeviceServiceTest.java | 57 ++++++++++-- .../service/BaseOtaPackageServiceTest.java | 90 ++++++++++++++----- .../dao/service/BaseTenantServiceTest.java | 45 ++++++++-- .../service/sql/OtaPackageServiceSqlTest.java | 1 + 15 files changed, 374 insertions(+), 85 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/ota/TbMultipartFileImp.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/ota/TbMultipartFile.java diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java index f74666f7ab..ad58df004a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java @@ -19,7 +19,12 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -29,6 +34,7 @@ import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.ota.util.ChecksumUtil; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import org.thingsboard.server.service.ota.TbMultipartFileImp; import java.io.IOException; @@ -61,7 +67,7 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen @Override public OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm, - MultipartFile file, User user) throws ThingsboardException { + MultipartFile file, User user) { TenantId tenantId = otaPackageInfo.getTenantId(); OtaPackageId otaPackageId = otaPackageInfo.getId(); try { @@ -83,7 +89,7 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen otaPackage.setContentType(file.getContentType()); otaPackage.setData(file.getInputStream()); otaPackage.setDataSize(file.getSize()); - OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); + OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage, new TbMultipartFileImp(file)); notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackage.getId(), savedOtaPackage, user, ActionType.UPDATED, true, null); return savedOtaPackage; diff --git a/application/src/main/java/org/thingsboard/server/service/ota/TbMultipartFileImp.java b/application/src/main/java/org/thingsboard/server/service/ota/TbMultipartFileImp.java new file mode 100644 index 0000000000..79cc158bea --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ota/TbMultipartFileImp.java @@ -0,0 +1,42 @@ +package org.thingsboard.server.service.ota; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.dao.ota.TbMultipartFile; + +import javax.validation.constraints.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +@RequiredArgsConstructor +public class TbMultipartFileImp implements TbMultipartFile { + + @NotNull + private final MultipartFile file; + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + } + + @Override + public String getContentType() { + return file.getContentType(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index edf43a5c74..e8da713c7d 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -101,6 +101,7 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.resource.TbResourceService; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Optional; @@ -599,16 +600,17 @@ public class DefaultTransportApiService implements TransportApiService { builder.setType(otaPackageInfo.getType().name()); builder.setTitle(otaPackageInfo.getTitle()); builder.setVersion(otaPackageInfo.getVersion()); - builder.setFileName(otaPackageInfo.getFileName()); + File file = otaPackageService.getOtaDataFile(otaPackageInfo.getTenantId(), otaPackageId); + builder.setFileName(file.getAbsolutePath()); builder.setContentType(otaPackageInfo.getContentType()); - if (!otaPackageDataCache.has(otaPackageId.toString())) { - OtaPackage otaPackage = otaPackageService.findOtaPackageById(tenantId, otaPackageId); - try { - otaPackageDataCache.put(otaPackageId.toString(), otaPackage.getData().readAllBytes()); - } catch (IOException e) { - log.error("Failed to cache ota package with id {}",otaPackage.getId(), e); - } - } +// if (!otaPackageDataCache.has(otaPackageId.toString())) { +// OtaPackage otaPackage = otaPackageService.findOtaPackageById(tenantId, otaPackageId); +// try { +// otaPackageDataCache.put(otaPackageId.toString(), otaPackage.getData().readAllBytes()); +// } catch (IOException e) { +// log.error("Failed to cache ota package with id {}",otaPackage.getId(), e); +// } +// } } } diff --git a/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java b/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java index e516d5e131..fbe5fa2310 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import org.junit.After; import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockMultipartFile; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNode; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNodeConfiguration; @@ -71,17 +72,23 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.ota.TbMultipartFile; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.BaseOtaPackageServiceTest; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; import org.thingsboard.server.service.sync.vc.data.SimpleEntitiesExportCtx; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; +import java.util.Optional; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -171,8 +178,9 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest otaPackage.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); otaPackage.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); otaPackage.setDataSize(1L); - otaPackage.setData(ByteBuffer.wrap(new byte[]{(int) 1})); - return otaPackageService.saveOtaPackage(otaPackage); + otaPackage.setData(new ByteArrayInputStream(new byte[]{1})); + MockMultipartFile file = new MockMultipartFile("filename.txt", new byte[]{1}); + return otaPackageService.saveOtaPackage(otaPackage, new TestTbMultipartFile(file)); } protected void checkImportedDeviceData(Device initialDevice, Device importedDevice) { @@ -449,4 +457,36 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest return new SecurityUser(user, true, new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail())); } + private class TestTbMultipartFile implements TbMultipartFile { + private final MockMultipartFile file; + + private TestTbMultipartFile(MockMultipartFile file) { + this.file = file; + } + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + + } + + @Override + public String getContentType() { + return file.getContentType(); + } + } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/ota/files/TemporaryFileCleaner.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/files/TemporaryFileCleaner.java index 77298dd3b9..92f19189bf 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/ota/files/TemporaryFileCleaner.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/files/TemporaryFileCleaner.java @@ -43,6 +43,7 @@ import java.util.stream.Collectors; public class TemporaryFileCleaner { @Value("${files.temporary_files_directory}/ota/") private String PATH; + private final static String FILE_NAME_TEMPLATE = "%s%s.tmp"; private final static long TEMPORARY_FILE_INACTIVITY_TIME = 900_000; private final ConcurrentMap lastActivityTimes = new ConcurrentHashMap<>(); @@ -51,19 +52,24 @@ public class TemporaryFileCleaner { } @EventListener(ApplicationReadyEvent.class) - public void createScheduledTaskToClear() { + public void cleanDirectoryWithTemporaryFiles() { createTempDirectoryIfNotExist(); - cleanDirectoryWithTemporaryFiles(); + cleanDirectory(); + log.info("Directory {} with temporary ota files cleaned", PATH); } private void createTempDirectoryIfNotExist() { File directory = new File(PATH); if (!directory.exists()) { - directory.mkdir(); + try{ + FileUtils.forceMkdir(directory); + } catch(IOException e){ + log.error("Failed to create directory for temporary files ", e); + } } } - private void cleanDirectoryWithTemporaryFiles() { + private void cleanDirectory() { File directory = new File(PATH); if (directory.isDirectory()) { File[] files = directory.listFiles(); @@ -100,7 +106,7 @@ public class TemporaryFileCleaner { } private synchronized void deleteFile(String otaId) { - String fileName = PATH + otaId; + String fileName = String.format(FILE_NAME_TEMPLATE, PATH, otaId); File file = new File(fileName); try (FileChannel channel = FileChannel.open(Path.of(URI.create(file.getPath())), StandardOpenOption.APPEND)) { FileLock lock = channel.lock(); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java index f8c9c452b0..304adba019 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.ota; import com.google.common.util.concurrent.ListenableFuture; +import org.springframework.boot.web.servlet.MultipartConfigFactory; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -32,7 +33,7 @@ public interface OtaPackageService { OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo, boolean isUrl); - OtaPackage saveOtaPackage(OtaPackage otaPackage); + OtaPackage saveOtaPackage(OtaPackage otaPackage, TbMultipartFile file); OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/TbMultipartFile.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/TbMultipartFile.java new file mode 100644 index 0000000000..1b92c6405f --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/TbMultipartFile.java @@ -0,0 +1,15 @@ +package org.thingsboard.server.dao.ota; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +public interface TbMultipartFile { + Optional getInputStream(); + + String getFileName(); + + long getFileSize(); + + String getContentType(); +} diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index de9619cb9f..ca991ff7de 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -20,10 +20,13 @@ import com.google.gson.JsonParser; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -65,6 +68,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.FileInputStream; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -505,15 +510,25 @@ public class DeviceApiController implements TbTransportService { } @Override + @SneakyThrows public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) { if (!TransportProtos.ResponseStatus.SUCCESS.equals(otaPackageResponseMsg.getResponseStatus())) { responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); } else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) { - String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString(); - ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck)); - ResponseEntity response = ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName()) - .header("x-filename", otaPackageResponseMsg.getFileName()) +// String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString(); + File file = new File(otaPackageResponseMsg.getFileName()); + Resource resource = null; + if(chuckSize!=0){ + FileInputStream fileInputStream = new FileInputStream(file); + byte[] bytes = new byte[chuckSize]; + fileInputStream.read(bytes, chuck, chuckSize); + resource = new ByteArrayResource(bytes); + } else { + resource= new FileSystemResource(file); + } + ResponseEntity response = ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + getFileName(otaPackageResponseMsg.getFileName())) + .header("x-filename", getFileName(otaPackageResponseMsg.getFileName())) .contentLength(resource.contentLength()) .contentType(parseMediaType(otaPackageResponseMsg.getContentType())) .body(resource); @@ -523,6 +538,10 @@ public class DeviceApiController implements TbTransportService { } } + private String getFileName(String fileName) { + return new File(fileName).getName(); + } + @Override public void onError(Throwable e) { log.warn("Failed to process request", e); diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index c655bd8df9..d7db3d30cd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -38,10 +38,7 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import javax.transaction.Transactional; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.*; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -98,25 +95,23 @@ public class BaseOtaPackageService extends AbstractCachedEntityService optionalStream = file.getInputStream(); + if(optionalStream.isEmpty()){ + log.error("Failed to get input stream from file {}", file.getFileName()); + throw new RuntimeException("Failed to save ota package file"); + } + BufferedInputStream stream = new BufferedInputStream(optionalStream.get()); stream.mark(0); otaPackage.setData(stream); OtaPackageId otaPackageId = otaPackage.getId(); if (otaPackageId != null) { publishEvictEvent(new OtaPackageCacheEvictEvent(otaPackageId)); } - OtaPackage savedOtaPackage = otaPackageDao.save(otaPackage.getTenantId(), otaPackage); - return savedOtaPackage; - } catch (FileNotFoundException e) { - log.error("Failed to save data of ota package {} to file", otaPackage.getId(), e); - throw new RuntimeException(e); + return otaPackageDao.save(otaPackage.getTenantId(), otaPackage); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("ota_package_tenant_title_version_unq_key")) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java index 069e1fcfc8..6ba4b40012 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AbstractHasOtaPackageValidator.java @@ -20,6 +20,7 @@ import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; 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.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -35,29 +36,29 @@ public abstract class AbstractHasOtaPackageValidator> exte protected void validateOtaPackage(TenantId tenantId, T entity, DeviceProfileId deviceProfileId) { if (entity.getFirmwareId() != null) { - OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, entity.getFirmwareId()); + OtaPackageInfo firmware = otaPackageService.findOtaPackageInfoById(tenantId, entity.getFirmwareId()); validateOtaPackage(tenantId, OtaPackageType.FIRMWARE, deviceProfileId, firmware); } if (entity.getSoftwareId() != null) { - OtaPackage software = otaPackageService.findOtaPackageById(tenantId, entity.getSoftwareId()); + OtaPackageInfo software = otaPackageService.findOtaPackageInfoById(tenantId, entity.getSoftwareId()); validateOtaPackage(tenantId, OtaPackageType.SOFTWARE, deviceProfileId, software); } } - private void validateOtaPackage(TenantId tenantId, OtaPackageType type, DeviceProfileId deviceProfileId, OtaPackage otaPackage) { - if (otaPackage == null) { + private void validateOtaPackage(TenantId tenantId, OtaPackageType type, DeviceProfileId deviceProfileId, OtaPackageInfo otaPackageInfo) { + if (otaPackageInfo == null) { throw new DataValidationException(prepareMsg("Can't assign non-existent %s!", type)); } - if (!otaPackage.getTenantId().equals(tenantId)) { + if (!otaPackageInfo.getTenantId().equals(tenantId)) { throw new DataValidationException(prepareMsg("Can't assign %s from different tenant!", type)); } - if (!otaPackage.getType().equals(type)) { - throw new DataValidationException(prepareMsg("Can't assign %s with type: " + otaPackage.getType(), type)); + if (!otaPackageInfo.getType().equals(type)) { + throw new DataValidationException(prepareMsg("Can't assign %s with type: " + otaPackageInfo.getType(), type)); } - if (otaPackage.getData() == null && !otaPackage.hasUrl()) { + if (otaPackageInfo.getDataSize() == 0 && !otaPackageInfo.hasUrl()) { throw new DataValidationException(prepareMsg("Can't assign %s with empty data!", type)); } - if (!otaPackage.getDeviceProfileId().equals(deviceProfileId)) { + if (!otaPackageInfo.getDeviceProfileId().equals(deviceProfileId)) { throw new DataValidationException(prepareMsg("Can't assign %s with different deviceProfile!", type)); } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index 0f84dbb9b7..309bd65d87 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -25,6 +25,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.springframework.mock.web.MockMultipartFile; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -37,12 +38,16 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.TbMultipartFile; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -115,7 +120,8 @@ public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(new ByteArrayInputStream(new byte[]{1})); firmware.setDataSize(1L); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile("test.txt", new byte[]{1}); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); deviceProfile.setFirmwareId(savedFirmware.getId()); @@ -261,7 +267,8 @@ public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); OtaPackage otaPackage = constructDefaultOtaPackage(tenantId, deviceProfile.getId()); - otaPackage = otaPackageService.saveOtaPackage(otaPackage); + MockMultipartFile file = new MockMultipartFile("filename.txt", new byte[]{1}); + otaPackage = otaPackageService.saveOtaPackage(otaPackage, new TestTbMultipartFile(file)); assertThat(deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId())).isNotNull(); assertThat(otaPackageService.findOtaPackageById(tenantId, otaPackage.getId())).isNotNull(); @@ -372,4 +379,36 @@ public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(1, pageData.getTotalElements()); } + private class TestTbMultipartFile implements TbMultipartFile { + private final MockMultipartFile file; + + private TestTbMultipartFile(MockMultipartFile file) { + this.file = file; + } + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + + } + + @Override + public String getContentType() { + return file.getContentType(); + } + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index acd7e0f229..2ca592d936 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -17,13 +17,13 @@ package org.thingsboard.server.dao.service; import com.datastax.oss.driver.api.core.uuid.Uuids; import org.apache.commons.lang3.RandomStringUtils; -import org.hibernate.engine.jdbc.BlobProxy; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.mock.web.MockMultipartFile; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; @@ -41,12 +41,15 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.TbMultipartFile; import java.io.ByteArrayInputStream; -import java.nio.ByteBuffer; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -204,7 +207,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(new ByteArrayInputStream(new byte[]{1})); firmware.setDataSize(1L); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile("test.txt", new byte[]{1}); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); savedDevice.setFirmwareId(savedFirmware.getId()); @@ -239,7 +243,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(new ByteArrayInputStream(new byte[]{1})); firmware.setDataSize(1L); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile("test.txt", new byte[]{1}); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); + savedDevice.setFirmwareId(savedFirmware.getId()); @@ -848,8 +854,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { deviceInfosWithLabel.stream() .anyMatch( d -> d.getId().equals(savedDevice.getId()) - && d.getTenantId().equals(tenantId) - && d.getLabel().equals(savedDevice.getLabel()) + && d.getTenantId().equals(tenantId) + && d.getLabel().equals(savedDevice.getLabel()) ) ); @@ -907,9 +913,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { deviceInfosWithLabel.stream() .anyMatch( d -> d.getId().equals(savedDevice.getId()) - && d.getTenantId().equals(tenantId) - && d.getDeviceProfileName().equals(savedDevice.getType()) - && d.getLabel().equals(savedDevice.getLabel()) + && d.getTenantId().equals(tenantId) + && d.getDeviceProfileName().equals(savedDevice.getType()) + && d.getLabel().equals(savedDevice.getLabel()) ) ); @@ -975,4 +981,37 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { ) ); } + + private class TestTbMultipartFile implements TbMultipartFile { + private final MockMultipartFile file; + + private TestTbMultipartFile(MockMultipartFile file) { + this.file = file; + } + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + + } + + @Override + public String getContentType() { + return file.getContentType(); + } + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index 00beb835d9..3c26c35eec 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.mock.web.MockMultipartFile; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -37,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.TbMultipartFile; import org.thingsboard.server.dao.ota.util.ChecksumUtil; import javax.validation.ValidationException; @@ -45,8 +47,10 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.FILE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; @@ -140,7 +144,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); firmware.setDataSize(DATA_SIZE); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); Assert.assertNotNull(savedFirmware); Assert.assertNotNull(savedFirmware.getId()); @@ -152,7 +157,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { Assert.assertEquals(firmware.getData(), savedFirmware.getData()); savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode()); - otaPackageService.saveOtaPackage(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware, new TestTbMultipartFile(file)); OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle()); @@ -219,7 +224,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setData(DATA); firmware.setDataSize(DATA_SIZE); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); savedFirmwareInfo = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmwareInfo.getId()); savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); @@ -249,7 +255,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage should be assigned to tenant!"); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test @@ -267,7 +274,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("Type should be specified!"); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test @@ -285,7 +293,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage title should be specified!"); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test @@ -303,8 +312,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage file name should be specified!"); - otaPackageService.saveOtaPackage(firmware); - } + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test public void testSaveFirmwareWithEmptyContentType() { @@ -321,8 +330,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage content type should be specified!"); - otaPackageService.saveOtaPackage(firmware); - } + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test public void testSaveFirmwareWithEmptyData() { @@ -339,8 +348,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage data should be specified!"); - otaPackageService.saveOtaPackage(firmware); - } + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test public void testSaveFirmwareWithInvalidTenant() { @@ -358,8 +367,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage is referencing to non-existent tenant!"); - otaPackageService.saveOtaPackage(firmware); - } + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test public void testSaveFirmwareWithInvalidDeviceProfileId() { @@ -377,7 +386,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage is referencing to non-existent device profile!"); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test @@ -395,7 +405,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage checksum should be specified!"); - otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); } @Test @@ -457,7 +468,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("Updating otaPackage deviceProfile is prohibited!"); savedFirmware.setDeviceProfileId(null); - otaPackageService.saveOtaPackage(savedFirmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + otaPackageService.saveOtaPackage(savedFirmware, new TestTbMultipartFile(file)); } finally { otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @@ -480,7 +492,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); firmware.setDataSize(DATA_SIZE); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware, new TestTbMultipartFile(file)); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); deviceProfileService.saveDeviceProfile(savedDeviceProfile); @@ -581,8 +594,9 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testFindTenantFirmwaresByTenantIdAndHasData() { List firmwares = new ArrayList<>(); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); for (int i = 0; i < 165; i++) { - firmwares.add(new OtaPackageInfo(otaPackageService.saveOtaPackage(createAndSaveFirmware(tenantId, VERSION + i)))); + firmwares.add(new OtaPackageInfo(otaPackageService.saveOtaPackage(createAndSaveFirmware(tenantId, VERSION + i), new TestTbMultipartFile(file)))); } OtaPackageInfo firmwareWithUrl = new OtaPackageInfo(); @@ -700,7 +714,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testGettingCorrectFileWithOtaData() { - OtaPackage firmware = createFirmware(tenantId, "24687846"); + OtaPackage firmware = createFirmware(tenantId, "24687846", deviceProfileId); File file = otaPackageService.getOtaDataFile(tenantId, firmware.getId()); try { assertEquals(firmware.getChecksum(), ChecksumUtil.generateChecksum(CHECKSUM_ALGORITHM, new FileInputStream(file))); @@ -710,7 +724,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { } private OtaPackage createAndSaveFirmware(TenantId tenantId, String version) { - return otaPackageService.saveOtaPackage(createFirmware(tenantId, version, deviceProfileId)); + MockMultipartFile file = new MockMultipartFile(FILE_NAME, new byte[]{1}); + return otaPackageService.saveOtaPackage(createFirmware(tenantId, version, deviceProfileId), new TestTbMultipartFile(file)); } public static OtaPackage createFirmware( @@ -732,4 +747,37 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setDataSize(DATA_SIZE); return firmware; } + + private class TestTbMultipartFile implements TbMultipartFile { + private final MockMultipartFile file; + + private TestTbMultipartFile(MockMultipartFile file) { + this.file = file; + } + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + + } + + @Override + public String getContentType() { + return file.getContentType(); + } + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java index 4a48c56157..f0bfd23bed 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTenantServiceTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.mock.web.MockMultipartFile; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.Customer; @@ -56,12 +57,12 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.ota.TbMultipartFile; import org.thingsboard.server.dao.tenant.TenantDao; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -584,9 +585,10 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest { } private OtaPackage createAndSaveOtaPackageFor(Tenant tenant, DeviceProfile deviceProfile) { + MockMultipartFile file = new MockMultipartFile("filename.txt", new byte[]{1}); return otaPackageService.saveOtaPackage( BaseOtaPackageServiceTest.createFirmware( - tenant.getId(), "2", deviceProfile.getId()) + tenant.getId(), "2", deviceProfile.getId()), new TestTbMultipartFile(file) ); } @@ -695,4 +697,37 @@ public abstract class BaseTenantServiceTest extends AbstractServiceTest { tenantProfile.setName("Test tenant profile"); return tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); } + + private class TestTbMultipartFile implements TbMultipartFile { + private final MockMultipartFile file; + + private TestTbMultipartFile(MockMultipartFile file) { + this.file = file; + } + + @Override + public Optional getInputStream() { + try { + return Optional.of(file.getInputStream()); + } catch (IOException e) { + return Optional.empty(); + } + } + + @Override + public String getFileName() { + return file.getName(); + } + + @Override + public long getFileSize() { + return file.getSize(); + + } + + @Override + public String getContentType() { + return file.getContentType(); + } + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java index 549e946eb1..ff31e4f5bb 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.service.sql; + import org.thingsboard.server.dao.service.BaseOtaPackageServiceTest; import org.thingsboard.server.dao.service.DaoSqlTest;