Commit 43b4f4461d22784ffd19a4fda38523c44dc926c7

Authored by YevhenBondarenko
1 parent b978f0f7

created data limits for resources and otaPackages, added url for the otaPackage

Showing 42 changed files with 491 additions and 189 deletions
@@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
67 type varchar(32) NOT NULL, 67 type varchar(32) NOT NULL,
68 title varchar(255) NOT NULL, 68 title varchar(255) NOT NULL,
69 version varchar(255) NOT NULL, 69 version varchar(255) NOT NULL,
  70 + url varchar(255),
70 file_name varchar(255), 71 file_name varchar(255),
71 content_type varchar(255), 72 content_type varchar(255),
72 checksum_algorithm varchar(32), 73 checksum_algorithm varchar(32),
@@ -60,7 +60,9 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -60,7 +60,9 @@ import org.thingsboard.server.dao.edge.EdgeService;
60 import org.thingsboard.server.dao.entityview.EntityViewService; 60 import org.thingsboard.server.dao.entityview.EntityViewService;
61 import org.thingsboard.server.dao.event.EventService; 61 import org.thingsboard.server.dao.event.EventService;
62 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; 62 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
  63 +import org.thingsboard.server.dao.ota.OtaPackageService;
63 import org.thingsboard.server.dao.relation.RelationService; 64 import org.thingsboard.server.dao.relation.RelationService;
  65 +import org.thingsboard.server.dao.resource.ResourceService;
64 import org.thingsboard.server.dao.rule.RuleChainService; 66 import org.thingsboard.server.dao.rule.RuleChainService;
65 import org.thingsboard.server.dao.rule.RuleNodeStateService; 67 import org.thingsboard.server.dao.rule.RuleNodeStateService;
66 import org.thingsboard.server.dao.tenant.TenantProfileService; 68 import org.thingsboard.server.dao.tenant.TenantProfileService;
@@ -311,6 +313,14 @@ public class ActorSystemContext { @@ -311,6 +313,14 @@ public class ActorSystemContext {
311 @Autowired(required = false) 313 @Autowired(required = false)
312 @Getter private EdgeRpcService edgeRpcService; 314 @Getter private EdgeRpcService edgeRpcService;
313 315
  316 + @Lazy
  317 + @Autowired(required = false)
  318 + @Getter private ResourceService resourceService;
  319 +
  320 + @Lazy
  321 + @Autowired(required = false)
  322 + @Getter private OtaPackageService otaPackageService;
  323 +
314 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 324 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
315 @Getter 325 @Getter
316 private long maxConcurrentSessionsPerDevice; 326 private long maxConcurrentSessionsPerDevice;
@@ -69,7 +69,9 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -69,7 +69,9 @@ import org.thingsboard.server.dao.edge.EdgeService;
69 import org.thingsboard.server.dao.entityview.EntityViewService; 69 import org.thingsboard.server.dao.entityview.EntityViewService;
70 import org.thingsboard.server.dao.nosql.CassandraStatementTask; 70 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
71 import org.thingsboard.server.dao.nosql.TbResultSetFuture; 71 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
  72 +import org.thingsboard.server.dao.ota.OtaPackageService;
72 import org.thingsboard.server.dao.relation.RelationService; 73 import org.thingsboard.server.dao.relation.RelationService;
  74 +import org.thingsboard.server.dao.resource.ResourceService;
73 import org.thingsboard.server.dao.rule.RuleChainService; 75 import org.thingsboard.server.dao.rule.RuleChainService;
74 import org.thingsboard.server.dao.tenant.TenantService; 76 import org.thingsboard.server.dao.tenant.TenantService;
75 import org.thingsboard.server.dao.timeseries.TimeseriesService; 77 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -487,6 +489,16 @@ class DefaultTbContext implements TbContext { @@ -487,6 +489,16 @@ class DefaultTbContext implements TbContext {
487 } 489 }
488 490
489 @Override 491 @Override
  492 + public ResourceService getResourceService() {
  493 + return mainCtx.getResourceService();
  494 + }
  495 +
  496 + @Override
  497 + public OtaPackageService getOtaPackageService() {
  498 + return mainCtx.getOtaPackageService();
  499 + }
  500 +
  501 + @Override
490 public RuleEngineDeviceProfileCache getDeviceProfileCache() { 502 public RuleEngineDeviceProfileCache getDeviceProfileCache() {
491 return mainCtx.getDeviceProfileCache(); 503 return mainCtx.getDeviceProfileCache();
492 } 504 }
@@ -64,6 +64,10 @@ public class OtaPackageController extends BaseController { @@ -64,6 +64,10 @@ public class OtaPackageController extends BaseController {
64 OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); 64 OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
65 OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); 65 OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ);
66 66
  67 + if (otaPackage.hasUrl()) {
  68 + return ResponseEntity.badRequest().build();
  69 + }
  70 +
67 ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); 71 ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array());
68 return ResponseEntity.ok() 72 return ResponseEntity.ok()
69 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) 73 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName())
@@ -182,11 +186,10 @@ public class OtaPackageController extends BaseController { @@ -182,11 +186,10 @@ public class OtaPackageController extends BaseController {
182 } 186 }
183 187
184 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 188 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
185 - @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) 189 + @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET)
186 @ResponseBody 190 @ResponseBody
187 public PageData<OtaPackageInfo> getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId, 191 public PageData<OtaPackageInfo> getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId,
188 @PathVariable("type") String strType, 192 @PathVariable("type") String strType,
189 - @PathVariable("hasData") boolean hasData,  
190 @RequestParam int pageSize, 193 @RequestParam int pageSize,
191 @RequestParam int page, 194 @RequestParam int page,
192 @RequestParam(required = false) String textSearch, 195 @RequestParam(required = false) String textSearch,
@@ -197,7 +200,7 @@ public class OtaPackageController extends BaseController { @@ -197,7 +200,7 @@ public class OtaPackageController extends BaseController {
197 try { 200 try {
198 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 201 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
199 return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), 202 return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
200 - new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), hasData, pageLink)); 203 + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink));
201 } catch (Exception e) { 204 } catch (Exception e) {
202 throw handleException(e); 205 throw handleException(e);
203 } 206 }
@@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.DataConstants; @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.DataConstants;
24 import org.thingsboard.server.common.data.Device; 24 import org.thingsboard.server.common.data.Device;
25 import org.thingsboard.server.common.data.DeviceProfile; 25 import org.thingsboard.server.common.data.DeviceProfile;
26 import org.thingsboard.server.common.data.OtaPackageInfo; 26 import org.thingsboard.server.common.data.OtaPackageInfo;
  27 +import org.thingsboard.server.common.data.StringUtils;
27 import org.thingsboard.server.common.data.id.DeviceId; 28 import org.thingsboard.server.common.data.id.DeviceId;
28 import org.thingsboard.server.common.data.id.OtaPackageId; 29 import org.thingsboard.server.common.data.id.OtaPackageId;
29 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
@@ -65,6 +66,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; @@ -65,6 +66,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE;
65 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; 66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
66 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; 67 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE;
67 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; 68 import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS;
  69 +import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL;
68 import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION; 70 import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION;
69 import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; 71 import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
70 import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; 72 import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
@@ -261,11 +263,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { @@ -261,11 +263,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
261 } 263 }
262 264
263 265
264 - private void update(Device device, OtaPackageInfo firmware, long ts) { 266 + private void update(Device device, OtaPackageInfo otaPackage, long ts) {
265 TenantId tenantId = device.getTenantId(); 267 TenantId tenantId = device.getTenantId();
266 DeviceId deviceId = device.getId(); 268 DeviceId deviceId = device.getId();
  269 + OtaPackageType otaPackageType = otaPackage.getType();
267 270
268 - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.INITIATED.name())); 271 + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name()));
269 272
270 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { 273 telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
271 @Override 274 @Override
@@ -280,11 +283,21 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { @@ -280,11 +283,21 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
280 }); 283 });
281 284
282 List<AttributeKvEntry> attributes = new ArrayList<>(); 285 List<AttributeKvEntry> attributes = new ArrayList<>();
283 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle())));  
284 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion())));  
285 - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize())));  
286 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm().name())));  
287 - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum()))); 286 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle())));
  287 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion())));
  288 + if (StringUtils.isEmpty(otaPackage.getUrl())) {
  289 + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize())));
  290 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name())));
  291 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum())));
  292 + remove(device, otaPackageType, Collections.singletonList(getAttributeKey(otaPackageType, URL)));
  293 + } else {
  294 + List<String> attrToRemove = new ArrayList<>();
  295 + attrToRemove.add(getAttributeKey(otaPackageType, SIZE));
  296 + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM));
  297 + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM));
  298 + remove(device, otaPackageType, attrToRemove);
  299 + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl())));
  300 + }
288 301
289 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { 302 telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() {
290 @Override 303 @Override
@@ -299,20 +312,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { @@ -299,20 +312,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
299 }); 312 });
300 } 313 }
301 314
302 - private void remove(Device device, OtaPackageType firmwareType) {  
303 - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType), 315 + private void remove(Device device, OtaPackageType otaPackageType) {
  316 + remove(device, otaPackageType, OtaPackageUtil.getAttributeKeys(otaPackageType));
  317 + }
  318 +
  319 + private void remove(Device device, OtaPackageType otaPackageType, List<String> attributesKeys) {
  320 + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, attributesKeys,
304 new FutureCallback<>() { 321 new FutureCallback<>() {
305 @Override 322 @Override
306 public void onSuccess(@Nullable Void tmp) { 323 public void onSuccess(@Nullable Void tmp) {
307 - log.trace("[{}] Success remove target firmware attributes!", device.getId()); 324 + log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType);
308 Set<AttributeKey> keysToNotify = new HashSet<>(); 325 Set<AttributeKey> keysToNotify = new HashSet<>();
309 - OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); 326 + attributesKeys.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
310 tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); 327 tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
311 } 328 }
312 329
313 @Override 330 @Override
314 public void onFailure(Throwable t) { 331 public void onFailure(Throwable t) {
315 - log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); 332 + log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t);
316 } 333 }
317 }); 334 });
318 } 335 }
@@ -157,6 +157,11 @@ public class DefaultTbResourceService implements TbResourceService { @@ -157,6 +157,11 @@ public class DefaultTbResourceService implements TbResourceService {
157 resourceService.deleteResourcesByTenantId(tenantId); 157 resourceService.deleteResourcesByTenantId(tenantId);
158 } 158 }
159 159
  160 + @Override
  161 + public long sumDataSizeByTenantId(TenantId tenantId) {
  162 + return resourceService.sumDataSizeByTenantId(tenantId);
  163 + }
  164 +
160 private Comparator<? super LwM2mObject> getComparator(String sortProperty, String sortOrder) { 165 private Comparator<? super LwM2mObject> getComparator(String sortProperty, String sortOrder) {
161 Comparator<LwM2mObject> comparator; 166 Comparator<LwM2mObject> comparator;
162 if ("name".equals(sortProperty)) { 167 if ("name".equals(sortProperty)) {
@@ -55,4 +55,5 @@ public interface TbResourceService { @@ -55,4 +55,5 @@ public interface TbResourceService {
55 55
56 void deleteResourcesByTenantId(TenantId tenantId); 56 void deleteResourcesByTenantId(TenantId tenantId);
57 57
  58 + long sumDataSizeByTenantId(TenantId tenantId);
58 } 59 }
@@ -536,6 +536,9 @@ public class DefaultTransportApiService implements TransportApiService { @@ -536,6 +536,9 @@ public class DefaultTransportApiService implements TransportApiService {
536 536
537 if (otaPackageInfo == null) { 537 if (otaPackageInfo == null) {
538 builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); 538 builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
  539 + } else if (otaPackageInfo.hasUrl()) {
  540 + builder.setResponseStatus(TransportProtos.ResponseStatus.FAILURE);
  541 + log.trace("[{}] Can`t send OtaPackage with URL data!", otaPackageInfo.getId());
539 } else { 542 } else {
540 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); 543 builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
541 builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits()); 544 builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits());
@@ -19,17 +19,24 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; @@ -19,17 +19,24 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
19 import org.junit.After; 19 import org.junit.After;
20 import org.junit.Assert; 20 import org.junit.Assert;
21 import org.junit.Before; 21 import org.junit.Before;
  22 +import org.junit.Rule;
22 import org.junit.Test; 23 import org.junit.Test;
  24 +import org.junit.rules.ExpectedException;
23 import org.springframework.beans.factory.annotation.Autowired; 25 import org.springframework.beans.factory.annotation.Autowired;
  26 +import org.thingsboard.server.common.data.EntityInfo;
  27 +import org.thingsboard.server.common.data.OtaPackage;
24 import org.thingsboard.server.common.data.ResourceType; 28 import org.thingsboard.server.common.data.ResourceType;
25 import org.thingsboard.server.common.data.TbResource; 29 import org.thingsboard.server.common.data.TbResource;
26 import org.thingsboard.server.common.data.TbResourceInfo; 30 import org.thingsboard.server.common.data.TbResourceInfo;
27 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
  32 +import org.thingsboard.server.common.data.TenantProfile;
28 import org.thingsboard.server.common.data.User; 33 import org.thingsboard.server.common.data.User;
  34 +import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
31 import org.thingsboard.server.common.data.page.PageLink; 37 import org.thingsboard.server.common.data.page.PageLink;
32 import org.thingsboard.server.common.data.security.Authority; 38 import org.thingsboard.server.common.data.security.Authority;
  39 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
33 import org.thingsboard.server.controller.AbstractControllerTest; 40 import org.thingsboard.server.controller.AbstractControllerTest;
34 import org.thingsboard.server.dao.exception.DataValidationException; 41 import org.thingsboard.server.dao.exception.DataValidationException;
35 import org.thingsboard.server.dao.service.DaoSqlTest; 42 import org.thingsboard.server.dao.service.DaoSqlTest;
@@ -109,6 +116,64 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { @@ -109,6 +116,64 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest {
109 .andExpect(status().isOk()); 116 .andExpect(status().isOk());
110 } 117 }
111 118
  119 + @Rule
  120 + public ExpectedException thrown = ExpectedException.none();
  121 +
  122 + @Test
  123 + public void testSaveResourceWithMaxSumDataSizeOutOfLimit() throws Exception {
  124 + loginSysAdmin();
  125 + long limit = 1;
  126 + EntityInfo defaultTenantProfileInfo = doGet("/api/tenantProfileInfo/default", EntityInfo.class);
  127 + TenantProfile defaultTenantProfile = doGet("/api/tenantProfile/" + defaultTenantProfileInfo.getId().getId().toString(), TenantProfile.class);
  128 + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(limit).build());
  129 + doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class);
  130 +
  131 + loginTenantAdmin();
  132 +
  133 + Assert.assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId));
  134 +
  135 + createResource("test", DEFAULT_FILE_NAME);
  136 +
  137 + Assert.assertEquals(1, resourceService.sumDataSizeByTenantId(tenantId));
  138 +
  139 + try {
  140 + thrown.expect(DataValidationException.class);
  141 + thrown.expectMessage(String.format("Failed to create the tb resource, files size limit is exhausted %d bytes!", limit));
  142 + createResource("test1", 1 + DEFAULT_FILE_NAME);
  143 + } finally {
  144 + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(0).build());
  145 + loginSysAdmin();
  146 + doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class);
  147 + }
  148 + }
  149 +
  150 + @Test
  151 + public void sumDataSizeByTenantId() throws ThingsboardException {
  152 + Assert.assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId));
  153 +
  154 + createResource("test", DEFAULT_FILE_NAME);
  155 + Assert.assertEquals(1, resourceService.sumDataSizeByTenantId(tenantId));
  156 +
  157 + int maxSumDataSize = 8;
  158 +
  159 + for (int i = 2; i <= maxSumDataSize; i++) {
  160 + createResource("test" + i, i + DEFAULT_FILE_NAME);
  161 + Assert.assertEquals(i, resourceService.sumDataSizeByTenantId(tenantId));
  162 + }
  163 +
  164 + Assert.assertEquals(maxSumDataSize, resourceService.sumDataSizeByTenantId(tenantId));
  165 + }
  166 +
  167 + private TbResource createResource(String title, String filename) throws ThingsboardException {
  168 + TbResource resource = new TbResource();
  169 + resource.setTenantId(tenantId);
  170 + resource.setTitle(title);
  171 + resource.setResourceType(ResourceType.JKS);
  172 + resource.setFileName(filename);
  173 + resource.setData("1");
  174 + return resourceService.saveResource(resource);
  175 + }
  176 +
112 @Test 177 @Test
113 public void testSaveTbResource() throws Exception { 178 public void testSaveTbResource() throws Exception {
114 TbResource resource = new TbResource(); 179 TbResource resource = new TbResource();
@@ -44,9 +44,11 @@ public interface OtaPackageService { @@ -44,9 +44,11 @@ public interface OtaPackageService {
44 44
45 PageData<OtaPackageInfo> findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink); 45 PageData<OtaPackageInfo> findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink);
46 46
47 - PageData<OtaPackageInfo> findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); 47 + PageData<OtaPackageInfo> findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink);
48 48
49 void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId); 49 void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId);
50 50
51 void deleteOtaPackagesByTenantId(TenantId tenantId); 51 void deleteOtaPackagesByTenantId(TenantId tenantId);
  52 +
  53 + long sumDataSizeByTenantId(TenantId tenantId);
52 } 54 }
@@ -49,5 +49,5 @@ public interface ResourceService { @@ -49,5 +49,5 @@ public interface ResourceService {
49 49
50 void deleteResourcesByTenantId(TenantId tenantId); 50 void deleteResourcesByTenantId(TenantId tenantId);
51 51
52 - 52 + long sumDataSizeByTenantId(TenantId tenantId);
53 } 53 }
@@ -37,8 +37,8 @@ public class OtaPackage extends OtaPackageInfo { @@ -37,8 +37,8 @@ public class OtaPackage extends OtaPackageInfo {
37 super(id); 37 super(id);
38 } 38 }
39 39
40 - public OtaPackage(OtaPackage firmware) {  
41 - super(firmware);  
42 - this.data = firmware.getData(); 40 + public OtaPackage(OtaPackage otaPackage) {
  41 + super(otaPackage);
  42 + this.data = otaPackage.getData();
43 } 43 }
44 } 44 }
@@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
37 private OtaPackageType type; 37 private OtaPackageType type;
38 private String title; 38 private String title;
39 private String version; 39 private String version;
  40 + private String url;
40 private boolean hasData; 41 private boolean hasData;
41 private String fileName; 42 private String fileName;
42 private String contentType; 43 private String contentType;
@@ -60,6 +61,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -60,6 +61,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
60 this.type = otaPackageInfo.getType(); 61 this.type = otaPackageInfo.getType();
61 this.title = otaPackageInfo.getTitle(); 62 this.title = otaPackageInfo.getTitle();
62 this.version = otaPackageInfo.getVersion(); 63 this.version = otaPackageInfo.getVersion();
  64 + this.url = otaPackageInfo.getUrl();
63 this.hasData = otaPackageInfo.isHasData(); 65 this.hasData = otaPackageInfo.isHasData();
64 this.fileName = otaPackageInfo.getFileName(); 66 this.fileName = otaPackageInfo.getFileName();
65 this.contentType = otaPackageInfo.getContentType(); 67 this.contentType = otaPackageInfo.getContentType();
@@ -78,4 +80,9 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -78,4 +80,9 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
78 public String getName() { 80 public String getName() {
79 return title; 81 return title;
80 } 82 }
  83 +
  84 + @JsonIgnore
  85 + public boolean hasUrl() {
  86 + return StringUtils.isNotEmpty(url);
  87 + }
81 } 88 }
@@ -19,7 +19,7 @@ import lombok.Getter; @@ -19,7 +19,7 @@ import lombok.Getter;
19 19
20 public enum OtaPackageKey { 20 public enum OtaPackageKey {
21 21
22 - TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"); 22 + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"), URL("url");
23 23
24 @Getter 24 @Getter
25 private final String value; 25 private final String value;
@@ -34,6 +34,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura @@ -34,6 +34,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
34 private long maxUsers; 34 private long maxUsers;
35 private long maxDashboards; 35 private long maxDashboards;
36 private long maxRuleChains; 36 private long maxRuleChains;
  37 + private long maxResourcesInBytes;
  38 + private long maxOtaPackagesInBytes;
37 39
38 private String transportTenantMsgRateLimit; 40 private String transportTenantMsgRateLimit;
39 private String transportTenantTelemetryMsgRateLimit; 41 private String transportTenantTelemetryMsgRateLimit;
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +
  20 +public interface TenantEntityWithDataDao {
  21 +
  22 + Long sumDataSizeByTenantId(TenantId tenantId);
  23 +}
@@ -487,6 +487,7 @@ public class ModelConstants { @@ -487,6 +487,7 @@ public class ModelConstants {
487 public static final String OTA_PACKAGE_TYPE_COLUMN = "type"; 487 public static final String OTA_PACKAGE_TYPE_COLUMN = "type";
488 public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY; 488 public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY;
489 public static final String OTA_PACKAGE_VERSION_COLUMN = "version"; 489 public static final String OTA_PACKAGE_VERSION_COLUMN = "version";
  490 + public static final String OTA_PACKAGE_URL_COLUMN = "url";
490 public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name"; 491 public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name";
491 public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type"; 492 public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type";
492 public static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm"; 493 public static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm";
@@ -51,6 +51,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_ @@ -51,6 +51,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_
51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; 51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN;
  54 +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_URL_COLUMN;
54 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; 55 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN;
55 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 56 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
56 57
@@ -77,6 +78,9 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -77,6 +78,9 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
77 @Column(name = OTA_PACKAGE_VERSION_COLUMN) 78 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
78 private String version; 79 private String version;
79 80
  81 + @Column(name = OTA_PACKAGE_URL_COLUMN)
  82 + private String url;
  83 +
80 @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) 84 @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN)
81 private String fileName; 85 private String fileName;
82 86
@@ -118,6 +122,7 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -118,6 +122,7 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
118 this.type = firmware.getType(); 122 this.type = firmware.getType();
119 this.title = firmware.getTitle(); 123 this.title = firmware.getTitle();
120 this.version = firmware.getVersion(); 124 this.version = firmware.getVersion();
  125 + this.url = firmware.getUrl();
121 this.fileName = firmware.getFileName(); 126 this.fileName = firmware.getFileName();
122 this.contentType = firmware.getContentType(); 127 this.contentType = firmware.getContentType();
123 this.checksumAlgorithm = firmware.getChecksumAlgorithm(); 128 this.checksumAlgorithm = firmware.getChecksumAlgorithm();
@@ -148,6 +153,7 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc @@ -148,6 +153,7 @@ public class OtaPackageEntity extends BaseSqlEntity<OtaPackage> implements Searc
148 firmware.setType(type); 153 firmware.setType(type);
149 firmware.setTitle(title); 154 firmware.setTitle(title);
150 firmware.setVersion(version); 155 firmware.setVersion(version);
  156 + firmware.setUrl(url);
151 firmware.setFileName(fileName); 157 firmware.setFileName(fileName);
152 firmware.setContentType(contentType); 158 firmware.setContentType(contentType);
153 firmware.setChecksumAlgorithm(checksumAlgorithm); 159 firmware.setChecksumAlgorithm(checksumAlgorithm);
@@ -22,6 +22,7 @@ import org.hibernate.annotations.Type; @@ -22,6 +22,7 @@ import org.hibernate.annotations.Type;
22 import org.hibernate.annotations.TypeDef; 22 import org.hibernate.annotations.TypeDef;
23 import org.thingsboard.common.util.JacksonUtil; 23 import org.thingsboard.common.util.JacksonUtil;
24 import org.thingsboard.server.common.data.OtaPackageInfo; 24 import org.thingsboard.server.common.data.OtaPackageInfo;
  25 +import org.thingsboard.server.common.data.StringUtils;
25 import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; 26 import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
26 import org.thingsboard.server.common.data.ota.OtaPackageType; 27 import org.thingsboard.server.common.data.ota.OtaPackageType;
27 import org.thingsboard.server.common.data.id.DeviceProfileId; 28 import org.thingsboard.server.common.data.id.DeviceProfileId;
@@ -50,6 +51,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_ @@ -50,6 +51,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_
50 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; 51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN;
51 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; 52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN;
52 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; 53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN;
  54 +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_URL_COLUMN;
53 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; 55 import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN;
54 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; 56 import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY;
55 57
@@ -76,6 +78,9 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -76,6 +78,9 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
76 @Column(name = OTA_PACKAGE_VERSION_COLUMN) 78 @Column(name = OTA_PACKAGE_VERSION_COLUMN)
77 private String version; 79 private String version;
78 80
  81 + @Column(name = OTA_PACKAGE_URL_COLUMN)
  82 + private String url;
  83 +
79 @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) 84 @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN)
80 private String fileName; 85 private String fileName;
81 86
@@ -116,6 +121,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -116,6 +121,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
116 } 121 }
117 this.title = firmware.getTitle(); 122 this.title = firmware.getTitle();
118 this.version = firmware.getVersion(); 123 this.version = firmware.getVersion();
  124 + this.url = firmware.getUrl();
119 this.fileName = firmware.getFileName(); 125 this.fileName = firmware.getFileName();
120 this.contentType = firmware.getContentType(); 126 this.contentType = firmware.getContentType();
121 this.checksumAlgorithm = firmware.getChecksumAlgorithm(); 127 this.checksumAlgorithm = firmware.getChecksumAlgorithm();
@@ -125,7 +131,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -125,7 +131,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
125 } 131 }
126 132
127 public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, 133 public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version,
128 - String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, 134 + String url, String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize,
129 Object additionalInfo, boolean hasData) { 135 Object additionalInfo, boolean hasData) {
130 this.id = id; 136 this.id = id;
131 this.createdTime = createdTime; 137 this.createdTime = createdTime;
@@ -134,6 +140,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -134,6 +140,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
134 this.type = type; 140 this.type = type;
135 this.title = title; 141 this.title = title;
136 this.version = version; 142 this.version = version;
  143 + this.url = url;
137 this.fileName = fileName; 144 this.fileName = fileName;
138 this.contentType = contentType; 145 this.contentType = contentType;
139 this.checksumAlgorithm = checksumAlgorithm; 146 this.checksumAlgorithm = checksumAlgorithm;
@@ -164,6 +171,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -164,6 +171,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
164 firmware.setType(type); 171 firmware.setType(type);
165 firmware.setTitle(title); 172 firmware.setTitle(title);
166 firmware.setVersion(version); 173 firmware.setVersion(version);
  174 + firmware.setUrl(url);
167 firmware.setFileName(fileName); 175 firmware.setFileName(fileName);
168 firmware.setContentType(contentType); 176 firmware.setContentType(contentType);
169 firmware.setChecksumAlgorithm(checksumAlgorithm); 177 firmware.setChecksumAlgorithm(checksumAlgorithm);
@@ -20,28 +20,32 @@ import com.google.common.hash.Hashing; @@ -20,28 +20,32 @@ import com.google.common.hash.Hashing;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 import lombok.RequiredArgsConstructor; 21 import lombok.RequiredArgsConstructor;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
23 -import org.apache.commons.lang3.StringUtils;  
24 import org.hibernate.exception.ConstraintViolationException; 23 import org.hibernate.exception.ConstraintViolationException;
  24 +import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.cache.Cache; 25 import org.springframework.cache.Cache;
26 import org.springframework.cache.CacheManager; 26 import org.springframework.cache.CacheManager;
27 import org.springframework.cache.annotation.Cacheable; 27 import org.springframework.cache.annotation.Cacheable;
  28 +import org.springframework.context.annotation.Lazy;
28 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
29 import org.thingsboard.server.cache.ota.OtaPackageDataCache; 30 import org.thingsboard.server.cache.ota.OtaPackageDataCache;
30 import org.thingsboard.server.common.data.DeviceProfile; 31 import org.thingsboard.server.common.data.DeviceProfile;
31 import org.thingsboard.server.common.data.OtaPackage; 32 import org.thingsboard.server.common.data.OtaPackage;
32 import org.thingsboard.server.common.data.OtaPackageInfo; 33 import org.thingsboard.server.common.data.OtaPackageInfo;
  34 +import org.thingsboard.server.common.data.StringUtils;
33 import org.thingsboard.server.common.data.Tenant; 35 import org.thingsboard.server.common.data.Tenant;
34 -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;  
35 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
36 import org.thingsboard.server.common.data.id.DeviceProfileId; 36 import org.thingsboard.server.common.data.id.DeviceProfileId;
37 import org.thingsboard.server.common.data.id.OtaPackageId; 37 import org.thingsboard.server.common.data.id.OtaPackageId;
38 import org.thingsboard.server.common.data.id.TenantId; 38 import org.thingsboard.server.common.data.id.TenantId;
  39 +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
  40 +import org.thingsboard.server.common.data.ota.OtaPackageType;
39 import org.thingsboard.server.common.data.page.PageData; 41 import org.thingsboard.server.common.data.page.PageData;
40 import org.thingsboard.server.common.data.page.PageLink; 42 import org.thingsboard.server.common.data.page.PageLink;
  43 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
41 import org.thingsboard.server.dao.device.DeviceProfileDao; 44 import org.thingsboard.server.dao.device.DeviceProfileDao;
42 import org.thingsboard.server.dao.exception.DataValidationException; 45 import org.thingsboard.server.dao.exception.DataValidationException;
43 import org.thingsboard.server.dao.service.DataValidator; 46 import org.thingsboard.server.dao.service.DataValidator;
44 import org.thingsboard.server.dao.service.PaginatedRemover; 47 import org.thingsboard.server.dao.service.PaginatedRemover;
  48 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
45 import org.thingsboard.server.dao.tenant.TenantDao; 49 import org.thingsboard.server.dao.tenant.TenantDao;
46 50
47 import java.nio.ByteBuffer; 51 import java.nio.ByteBuffer;
@@ -50,6 +54,7 @@ import java.util.List; @@ -50,6 +54,7 @@ import java.util.List;
50 import java.util.Optional; 54 import java.util.Optional;
51 55
52 import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; 56 import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE;
  57 +import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE;
53 import static org.thingsboard.server.dao.service.Validator.validateId; 58 import static org.thingsboard.server.dao.service.Validator.validateId;
54 import static org.thingsboard.server.dao.service.Validator.validatePageLink; 59 import static org.thingsboard.server.dao.service.Validator.validatePageLink;
55 60
@@ -67,6 +72,10 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -67,6 +72,10 @@ public class BaseOtaPackageService implements OtaPackageService {
67 private final CacheManager cacheManager; 72 private final CacheManager cacheManager;
68 private final OtaPackageDataCache otaPackageDataCache; 73 private final OtaPackageDataCache otaPackageDataCache;
69 74
  75 + @Autowired
  76 + @Lazy
  77 + private TbTenantProfileCache tenantProfileCache;
  78 +
70 @Override 79 @Override
71 public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { 80 public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) {
72 log.trace("Executing saveOtaPackageInfo [{}]", otaPackageInfo); 81 log.trace("Executing saveOtaPackageInfo [{}]", otaPackageInfo);
@@ -172,11 +181,11 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -172,11 +181,11 @@ public class BaseOtaPackageService implements OtaPackageService {
172 } 181 }
173 182
174 @Override 183 @Override
175 - public PageData<OtaPackageInfo> findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) {  
176 - log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); 184 + public PageData<OtaPackageInfo> findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) {
  185 + log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], pageLink [{}]", tenantId, pageLink);
177 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 186 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
178 validatePageLink(pageLink); 187 validatePageLink(pageLink);
179 - return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, hasData, pageLink); 188 + return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, pageLink);
180 } 189 }
181 190
182 @Override 191 @Override
@@ -205,6 +214,11 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -205,6 +214,11 @@ public class BaseOtaPackageService implements OtaPackageService {
205 } 214 }
206 215
207 @Override 216 @Override
  217 + public long sumDataSizeByTenantId(TenantId tenantId) {
  218 + return otaPackageDao.sumDataSizeByTenantId(tenantId);
  219 + }
  220 +
  221 + @Override
208 public void deleteOtaPackagesByTenantId(TenantId tenantId) { 222 public void deleteOtaPackagesByTenantId(TenantId tenantId) {
209 log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId); 223 log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId);
210 validateId(tenantId, INCORRECT_TENANT_ID + tenantId); 224 validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
@@ -228,30 +242,42 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -228,30 +242,42 @@ public class BaseOtaPackageService implements OtaPackageService {
228 private DataValidator<OtaPackage> otaPackageValidator = new DataValidator<>() { 242 private DataValidator<OtaPackage> otaPackageValidator = new DataValidator<>() {
229 243
230 @Override 244 @Override
  245 + protected void validateCreate(TenantId tenantId, OtaPackage otaPackage) {
  246 + DefaultTenantProfileConfiguration profileConfiguration =
  247 + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  248 + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes();
  249 + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE);
  250 + }
  251 +
  252 + @Override
231 protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { 253 protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) {
232 validateImpl(otaPackage); 254 validateImpl(otaPackage);
233 255
234 - if (StringUtils.isEmpty(otaPackage.getFileName())) {  
235 - throw new DataValidationException("OtaPackage file name should be specified!");  
236 - } 256 + if (StringUtils.isEmpty(otaPackage.getUrl())) {
  257 + if (StringUtils.isEmpty(otaPackage.getFileName())) {
  258 + throw new DataValidationException("OtaPackage file name should be specified!");
  259 + }
237 260
238 - if (StringUtils.isEmpty(otaPackage.getContentType())) {  
239 - throw new DataValidationException("OtaPackage content type should be specified!");  
240 - } 261 + if (StringUtils.isEmpty(otaPackage.getContentType())) {
  262 + throw new DataValidationException("OtaPackage content type should be specified!");
  263 + }
241 264
242 - if (otaPackage.getChecksumAlgorithm() == null) {  
243 - throw new DataValidationException("OtaPackage checksum algorithm should be specified!");  
244 - }  
245 - if (StringUtils.isEmpty(otaPackage.getChecksum())) {  
246 - throw new DataValidationException("OtaPackage checksum should be specified!");  
247 - } 265 + if (otaPackage.getChecksumAlgorithm() == null) {
  266 + throw new DataValidationException("OtaPackage checksum algorithm should be specified!");
  267 + }
  268 + if (StringUtils.isEmpty(otaPackage.getChecksum())) {
  269 + throw new DataValidationException("OtaPackage checksum should be specified!");
  270 + }
248 271
249 - String currentChecksum; 272 + String currentChecksum;
250 273
251 - currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); 274 + currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData());
252 275
253 - if (!currentChecksum.equals(otaPackage.getChecksum())) {  
254 - throw new DataValidationException("Wrong otaPackage file!"); 276 + if (!currentChecksum.equals(otaPackage.getChecksum())) {
  277 + throw new DataValidationException("Wrong otaPackage file!");
  278 + }
  279 + } else {
  280 + //TODO: validate url
255 } 281 }
256 } 282 }
257 283
@@ -264,6 +290,13 @@ public class BaseOtaPackageService implements OtaPackageService { @@ -264,6 +290,13 @@ public class BaseOtaPackageService implements OtaPackageService {
264 if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { 290 if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) {
265 throw new DataValidationException("Updating otaPackage data is prohibited!"); 291 throw new DataValidationException("Updating otaPackage data is prohibited!");
266 } 292 }
  293 +
  294 + if (otaPackageOld.getData() == null && otaPackage.getData() != null) {
  295 + DefaultTenantProfileConfiguration profileConfiguration =
  296 + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  297 + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes();
  298 + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE);
  299 + }
267 } 300 }
268 }; 301 };
269 302
@@ -16,8 +16,11 @@ @@ -16,8 +16,11 @@
16 package org.thingsboard.server.dao.ota; 16 package org.thingsboard.server.dao.ota;
17 17
18 import org.thingsboard.server.common.data.OtaPackage; 18 import org.thingsboard.server.common.data.OtaPackage;
  19 +import org.thingsboard.server.common.data.id.TenantId;
19 import org.thingsboard.server.dao.Dao; 20 import org.thingsboard.server.dao.Dao;
  21 +import org.thingsboard.server.dao.TenantEntityDao;
  22 +import org.thingsboard.server.dao.TenantEntityWithDataDao;
20 23
21 -public interface OtaPackageDao extends Dao<OtaPackage> {  
22 - 24 +public interface OtaPackageDao extends Dao<OtaPackage>, TenantEntityWithDataDao {
  25 + Long sumDataSizeByTenantId(TenantId tenantId);
23 } 26 }
@@ -28,7 +28,7 @@ public interface OtaPackageInfoDao extends Dao<OtaPackageInfo> { @@ -28,7 +28,7 @@ public interface OtaPackageInfoDao extends Dao<OtaPackageInfo> {
28 28
29 PageData<OtaPackageInfo> findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink); 29 PageData<OtaPackageInfo> findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink);
30 30
31 - PageData<OtaPackageInfo> findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); 31 + PageData<OtaPackageInfo> findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink);
32 32
33 boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId); 33 boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId);
34 34
@@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 import org.apache.commons.lang3.StringUtils; 20 import org.apache.commons.lang3.StringUtils;
21 import org.hibernate.exception.ConstraintViolationException; 21 import org.hibernate.exception.ConstraintViolationException;
  22 +import org.springframework.context.annotation.Lazy;
22 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
23 import org.thingsboard.server.common.data.ResourceType; 24 import org.thingsboard.server.common.data.ResourceType;
24 import org.thingsboard.server.common.data.TbResource; 25 import org.thingsboard.server.common.data.TbResource;
@@ -28,16 +29,19 @@ import org.thingsboard.server.common.data.id.TbResourceId; @@ -28,16 +29,19 @@ import org.thingsboard.server.common.data.id.TbResourceId;
28 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.page.PageData; 30 import org.thingsboard.server.common.data.page.PageData;
30 import org.thingsboard.server.common.data.page.PageLink; 31 import org.thingsboard.server.common.data.page.PageLink;
  32 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
31 import org.thingsboard.server.dao.exception.DataValidationException; 33 import org.thingsboard.server.dao.exception.DataValidationException;
32 import org.thingsboard.server.dao.model.ModelConstants; 34 import org.thingsboard.server.dao.model.ModelConstants;
33 import org.thingsboard.server.dao.service.DataValidator; 35 import org.thingsboard.server.dao.service.DataValidator;
34 import org.thingsboard.server.dao.service.PaginatedRemover; 36 import org.thingsboard.server.dao.service.PaginatedRemover;
35 import org.thingsboard.server.dao.service.Validator; 37 import org.thingsboard.server.dao.service.Validator;
  38 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
36 import org.thingsboard.server.dao.tenant.TenantDao; 39 import org.thingsboard.server.dao.tenant.TenantDao;
37 40
38 import java.util.List; 41 import java.util.List;
39 import java.util.Optional; 42 import java.util.Optional;
40 43
  44 +import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE;
41 import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; 45 import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
42 import static org.thingsboard.server.dao.service.Validator.validateId; 46 import static org.thingsboard.server.dao.service.Validator.validateId;
43 47
@@ -49,12 +53,13 @@ public class BaseResourceService implements ResourceService { @@ -49,12 +53,13 @@ public class BaseResourceService implements ResourceService {
49 private final TbResourceDao resourceDao; 53 private final TbResourceDao resourceDao;
50 private final TbResourceInfoDao resourceInfoDao; 54 private final TbResourceInfoDao resourceInfoDao;
51 private final TenantDao tenantDao; 55 private final TenantDao tenantDao;
  56 + private final TbTenantProfileCache tenantProfileCache;
52 57
53 -  
54 - public BaseResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao) { 58 + public BaseResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao, @Lazy TbTenantProfileCache tenantProfileCache) {
55 this.resourceDao = resourceDao; 59 this.resourceDao = resourceDao;
56 this.resourceInfoDao = resourceInfoDao; 60 this.resourceInfoDao = resourceInfoDao;
57 this.tenantDao = tenantDao; 61 this.tenantDao = tenantDao;
  62 + this.tenantProfileCache = tenantProfileCache;
58 } 63 }
59 64
60 @Override 65 @Override
@@ -143,9 +148,24 @@ public class BaseResourceService implements ResourceService { @@ -143,9 +148,24 @@ public class BaseResourceService implements ResourceService {
143 tenantResourcesRemover.removeEntities(tenantId, tenantId); 148 tenantResourcesRemover.removeEntities(tenantId, tenantId);
144 } 149 }
145 150
  151 + @Override
  152 + public long sumDataSizeByTenantId(TenantId tenantId) {
  153 + return resourceDao.sumDataSizeByTenantId(tenantId);
  154 + }
  155 +
146 private DataValidator<TbResource> resourceValidator = new DataValidator<>() { 156 private DataValidator<TbResource> resourceValidator = new DataValidator<>() {
147 157
148 @Override 158 @Override
  159 + protected void validateCreate(TenantId tenantId, TbResource resource) {
  160 + if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId) ) {
  161 + DefaultTenantProfileConfiguration profileConfiguration =
  162 + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
  163 + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes();
  164 + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE);
  165 + }
  166 + }
  167 +
  168 + @Override
149 protected void validateDataImpl(TenantId tenantId, TbResource resource) { 169 protected void validateDataImpl(TenantId tenantId, TbResource resource) {
150 if (StringUtils.isEmpty(resource.getTitle())) { 170 if (StringUtils.isEmpty(resource.getTitle())) {
151 throw new DataValidationException("Resource title should be specified!"); 171 throw new DataValidationException("Resource title should be specified!");
@@ -21,10 +21,11 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -21,10 +21,11 @@ import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.data.page.PageData; 21 import org.thingsboard.server.common.data.page.PageData;
22 import org.thingsboard.server.common.data.page.PageLink; 22 import org.thingsboard.server.common.data.page.PageLink;
23 import org.thingsboard.server.dao.Dao; 23 import org.thingsboard.server.dao.Dao;
  24 +import org.thingsboard.server.dao.TenantEntityWithDataDao;
24 25
25 import java.util.List; 26 import java.util.List;
26 27
27 -public interface TbResourceDao extends Dao<TbResource> { 28 +public interface TbResourceDao extends Dao<TbResource>, TenantEntityWithDataDao {
28 29
29 TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); 30 TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId);
30 31
@@ -23,8 +23,10 @@ import org.hibernate.validator.cfg.ConstraintMapping; @@ -23,8 +23,10 @@ import org.hibernate.validator.cfg.ConstraintMapping;
23 import org.thingsboard.server.common.data.BaseData; 23 import org.thingsboard.server.common.data.BaseData;
24 import org.thingsboard.server.common.data.EntityType; 24 import org.thingsboard.server.common.data.EntityType;
25 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
26 import org.thingsboard.server.common.data.validation.NoXss; 27 import org.thingsboard.server.common.data.validation.NoXss;
27 import org.thingsboard.server.dao.TenantEntityDao; 28 import org.thingsboard.server.dao.TenantEntityDao;
  29 +import org.thingsboard.server.dao.TenantEntityWithDataDao;
28 import org.thingsboard.server.dao.exception.DataValidationException; 30 import org.thingsboard.server.dao.exception.DataValidationException;
29 31
30 import javax.validation.ConstraintViolation; 32 import javax.validation.ConstraintViolation;
@@ -123,6 +125,19 @@ public abstract class DataValidator<D extends BaseData<?>> { @@ -123,6 +125,19 @@ public abstract class DataValidator<D extends BaseData<?>> {
123 } 125 }
124 } 126 }
125 127
  128 + protected void validateMaxSumDataSizePerTenant(TenantId tenantId,
  129 + TenantEntityWithDataDao dataDao,
  130 + long maxSumDataSize,
  131 + long currentDataSize,
  132 + EntityType entityType) {
  133 + if (maxSumDataSize > 0) {
  134 + if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) {
  135 + throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!",
  136 + entityType.name().toLowerCase().replaceAll("_", " "), maxSumDataSize));
  137 + }
  138 + }
  139 + }
  140 +
126 protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { 141 protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) {
127 Set<String> expectedFields = new HashSet<>(); 142 Set<String> expectedFields = new HashSet<>();
128 Iterator<String> fieldsIterator = expectedNode.fieldNames(); 143 Iterator<String> fieldsIterator = expectedNode.fieldNames();
@@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.data.repository.CrudRepository; 20 import org.springframework.data.repository.CrudRepository;
21 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
22 import org.thingsboard.server.common.data.OtaPackage; 22 import org.thingsboard.server.common.data.OtaPackage;
  23 +import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.dao.ota.OtaPackageDao; 24 import org.thingsboard.server.dao.ota.OtaPackageDao;
24 import org.thingsboard.server.dao.model.sql.OtaPackageEntity; 25 import org.thingsboard.server.dao.model.sql.OtaPackageEntity;
25 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 26 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
@@ -43,4 +44,8 @@ public class JpaOtaPackageDao extends JpaAbstractSearchTextDao<OtaPackageEntity, @@ -43,4 +44,8 @@ public class JpaOtaPackageDao extends JpaAbstractSearchTextDao<OtaPackageEntity,
43 return otaPackageRepository; 44 return otaPackageRepository;
44 } 45 }
45 46
  47 + @Override
  48 + public Long sumDataSizeByTenantId(TenantId tenantId) {
  49 + return otaPackageRepository.sumDataSizeByTenantId(tenantId.getId());
  50 + }
46 } 51 }
@@ -76,13 +76,12 @@ public class JpaOtaPackageInfoDao extends JpaAbstractSearchTextDao<OtaPackageInf @@ -76,13 +76,12 @@ public class JpaOtaPackageInfoDao extends JpaAbstractSearchTextDao<OtaPackageInf
76 } 76 }
77 77
78 @Override 78 @Override
79 - public PageData<OtaPackageInfo> findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { 79 + public PageData<OtaPackageInfo> findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) {
80 return DaoUtil.toPageData(otaPackageInfoRepository 80 return DaoUtil.toPageData(otaPackageInfoRepository
81 .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( 81 .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(
82 tenantId.getId(), 82 tenantId.getId(),
83 deviceProfileId.getId(), 83 deviceProfileId.getId(),
84 otaPackageType, 84 otaPackageType,
85 - hasData,  
86 Objects.toString(pageLink.getTextSearch(), ""), 85 Objects.toString(pageLink.getTextSearch(), ""),
87 DaoUtil.toPageable(pageLink))); 86 DaoUtil.toPageable(pageLink)));
88 } 87 }
@@ -26,27 +26,26 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; @@ -26,27 +26,26 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity;
26 import java.util.UUID; 26 import java.util.UUID;
27 27
28 public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoEntity, UUID> { 28 public interface OtaPackageInfoRepository extends CrudRepository<OtaPackageInfoEntity, UUID> {
29 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + 29 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE " +
30 "f.tenantId = :tenantId " + 30 "f.tenantId = :tenantId " +
31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 31 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
32 Page<OtaPackageInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, 32 Page<OtaPackageInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId,
33 @Param("searchText") String searchText, 33 @Param("searchText") String searchText,
34 Pageable pageable); 34 Pageable pageable);
35 35
36 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + 36 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, true) FROM OtaPackageEntity f WHERE " +
37 "f.tenantId = :tenantId " + 37 "f.tenantId = :tenantId " +
38 "AND f.deviceProfileId = :deviceProfileId " + 38 "AND f.deviceProfileId = :deviceProfileId " +
39 "AND f.type = :type " + 39 "AND f.type = :type " +
40 - "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + 40 + "AND (f.data IS NOT NULL OR f.url IS NOT NULL) " +
41 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") 41 "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
42 Page<OtaPackageInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, 42 Page<OtaPackageInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId,
43 @Param("deviceProfileId") UUID deviceProfileId, 43 @Param("deviceProfileId") UUID deviceProfileId,
44 @Param("type") OtaPackageType type, 44 @Param("type") OtaPackageType type,
45 - @Param("hasData") boolean hasData,  
46 @Param("searchText") String searchText, 45 @Param("searchText") String searchText,
47 Pageable pageable); 46 Pageable pageable);
48 47
49 - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE f.id = :id") 48 + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE f.id = :id")
50 OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id); 49 OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id);
51 50
52 @Query(value = "SELECT exists(SELECT * " + 51 @Query(value = "SELECT exists(SELECT * " +
@@ -15,10 +15,15 @@ @@ -15,10 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sql.ota; 16 package org.thingsboard.server.dao.sql.ota;
17 17
  18 +import org.springframework.data.jpa.repository.Query;
18 import org.springframework.data.repository.CrudRepository; 19 import org.springframework.data.repository.CrudRepository;
  20 +import org.springframework.data.repository.query.Param;
19 import org.thingsboard.server.dao.model.sql.OtaPackageEntity; 21 import org.thingsboard.server.dao.model.sql.OtaPackageEntity;
  22 +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity;
20 23
21 import java.util.UUID; 24 import java.util.UUID;
22 25
23 public interface OtaPackageRepository extends CrudRepository<OtaPackageEntity, UUID> { 26 public interface OtaPackageRepository extends CrudRepository<OtaPackageEntity, UUID> {
  27 + @Query(value = "SELECT COALESCE(SUM(ota.data_size), 0) FROM ota_package ota WHERE ota.tenant_id = :tenantId AND ota.data IS NOT NULL", nativeQuery = true)
  28 + Long sumDataSizeByTenantId(@Param("tenantId") UUID tenantId);
24 } 29 }
@@ -92,4 +92,8 @@ public class JpaTbResourceDao extends JpaAbstractSearchTextDao<TbResourceEntity, @@ -92,4 +92,8 @@ public class JpaTbResourceDao extends JpaAbstractSearchTextDao<TbResourceEntity,
92 resourceType.name(), objectIds)); 92 resourceType.name(), objectIds));
93 } 93 }
94 94
  95 + @Override
  96 + public Long sumDataSizeByTenantId(TenantId tenantId) {
  97 + return resourceRepository.sumDataSizeByTenantId(tenantId.getId());
  98 + }
95 } 99 }
@@ -77,4 +77,7 @@ public interface TbResourceRepository extends CrudRepository<TbResourceEntity, U @@ -77,4 +77,7 @@ public interface TbResourceRepository extends CrudRepository<TbResourceEntity, U
77 @Param("systemAdminId") UUID sysAdminId, 77 @Param("systemAdminId") UUID sysAdminId,
78 @Param("resourceType") String resourceType, 78 @Param("resourceType") String resourceType,
79 @Param("resourceIds") String[] objectIds); 79 @Param("resourceIds") String[] objectIds);
  80 +
  81 + @Query(value = "SELECT COALESCE(SUM(LENGTH(r.data)), 0) FROM resource r WHERE r.tenant_id = :tenantId", nativeQuery = true)
  82 + Long sumDataSizeByTenantId(@Param("tenantId") UUID tenantId);
80 } 83 }
@@ -168,6 +168,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -168,6 +168,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
168 type varchar(32) NOT NULL, 168 type varchar(32) NOT NULL,
169 title varchar(255) NOT NULL, 169 title varchar(255) NOT NULL,
170 version varchar(255) NOT NULL, 170 version varchar(255) NOT NULL,
  171 + url varchar(255),
171 file_name varchar(255), 172 file_name varchar(255),
172 content_type varchar(255), 173 content_type varchar(255),
173 checksum_algorithm varchar(32), 174 checksum_algorithm varchar(32),
@@ -186,6 +186,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( @@ -186,6 +186,7 @@ CREATE TABLE IF NOT EXISTS ota_package (
186 type varchar(32) NOT NULL, 186 type varchar(32) NOT NULL,
187 title varchar(255) NOT NULL, 187 title varchar(255) NOT NULL,
188 version varchar(255) NOT NULL, 188 version varchar(255) NOT NULL,
  189 + url varchar(255),
189 file_name varchar(255), 190 file_name varchar(255),
190 content_type varchar(255), 191 content_type varchar(255),
191 checksum_algorithm varchar(32), 192 checksum_algorithm varchar(32),
@@ -24,10 +24,10 @@ import java.util.Arrays; @@ -24,10 +24,10 @@ import java.util.Arrays;
24 24
25 @RunWith(ClasspathSuite.class) 25 @RunWith(ClasspathSuite.class)
26 @ClassnameFilters({ 26 @ClassnameFilters({
27 - "org.thingsboard.server.dao.service.sql.*SqlTest",  
28 - "org.thingsboard.server.dao.service.attributes.sql.*SqlTest",  
29 - "org.thingsboard.server.dao.service.event.sql.*SqlTest",  
30 - "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest" 27 + "org.thingsboard.server.dao.service.sql.OtaPackageServiceSqlTest",
  28 +// "org.thingsboard.server.dao.service.attributes.sql.*SqlTest",
  29 +// "org.thingsboard.server.dao.service.event.sql.*SqlTest",
  30 +// "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest"
31 31
32 }) 32 })
33 public class SqlDaoServiceTestSuite { 33 public class SqlDaoServiceTestSuite {
@@ -59,6 +59,7 @@ import org.thingsboard.server.dao.relation.RelationService; @@ -59,6 +59,7 @@ import org.thingsboard.server.dao.relation.RelationService;
59 import org.thingsboard.server.dao.resource.ResourceService; 59 import org.thingsboard.server.dao.resource.ResourceService;
60 import org.thingsboard.server.dao.rule.RuleChainService; 60 import org.thingsboard.server.dao.rule.RuleChainService;
61 import org.thingsboard.server.dao.settings.AdminSettingsService; 61 import org.thingsboard.server.dao.settings.AdminSettingsService;
  62 +import org.thingsboard.server.dao.tenant.DefaultTbTenantProfileCache;
62 import org.thingsboard.server.dao.tenant.TenantProfileService; 63 import org.thingsboard.server.dao.tenant.TenantProfileService;
63 import org.thingsboard.server.dao.tenant.TenantService; 64 import org.thingsboard.server.dao.tenant.TenantService;
64 import org.thingsboard.server.dao.timeseries.TimeseriesService; 65 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -158,10 +159,12 @@ public abstract class AbstractServiceTest { @@ -158,10 +159,12 @@ public abstract class AbstractServiceTest {
158 @Autowired 159 @Autowired
159 protected ResourceService resourceService; 160 protected ResourceService resourceService;
160 161
161 -  
162 @Autowired 162 @Autowired
163 protected OtaPackageService otaPackageService; 163 protected OtaPackageService otaPackageService;
164 164
  165 + @Autowired
  166 + protected DefaultTbTenantProfileCache tenantProfileCache;
  167 +
165 public class IdComparator<D extends HasId> implements Comparator<D> { 168 public class IdComparator<D extends HasId> implements Comparator<D> {
166 @Override 169 @Override
167 public int compare(D o1, D o2) { 170 public int compare(D o1, D o2) {
@@ -28,11 +28,13 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -28,11 +28,13 @@ import org.thingsboard.server.common.data.DeviceProfile;
28 import org.thingsboard.server.common.data.OtaPackage; 28 import org.thingsboard.server.common.data.OtaPackage;
29 import org.thingsboard.server.common.data.OtaPackageInfo; 29 import org.thingsboard.server.common.data.OtaPackageInfo;
30 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
31 -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; 31 +import org.thingsboard.server.common.data.TenantProfile;
32 import org.thingsboard.server.common.data.id.DeviceProfileId; 32 import org.thingsboard.server.common.data.id.DeviceProfileId;
33 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
  34 +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
34 import org.thingsboard.server.common.data.page.PageData; 35 import org.thingsboard.server.common.data.page.PageData;
35 import org.thingsboard.server.common.data.page.PageLink; 36 import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
36 import org.thingsboard.server.dao.exception.DataValidationException; 38 import org.thingsboard.server.dao.exception.DataValidationException;
37 39
38 import java.nio.ByteBuffer; 40 import java.nio.ByteBuffer;
@@ -50,7 +52,9 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -50,7 +52,9 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
50 private static final String CONTENT_TYPE = "text/plain"; 52 private static final String CONTENT_TYPE = "text/plain";
51 private static final ChecksumAlgorithm CHECKSUM_ALGORITHM = ChecksumAlgorithm.SHA256; 53 private static final ChecksumAlgorithm CHECKSUM_ALGORITHM = ChecksumAlgorithm.SHA256;
52 private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; 54 private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a";
53 - private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); 55 + private static final long DATA_SIZE = 1L;
  56 + private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{(int) DATA_SIZE});
  57 + private static final String URL = "http://firmware.test.org";
54 58
55 private IdComparator<OtaPackageInfo> idComparator = new IdComparator<>(); 59 private IdComparator<OtaPackageInfo> idComparator = new IdComparator<>();
56 60
@@ -78,6 +82,41 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -78,6 +82,41 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
78 @After 82 @After
79 public void after() { 83 public void after() {
80 tenantService.deleteTenant(tenantId); 84 tenantService.deleteTenant(tenantId);
  85 + tenantProfileService.deleteTenantProfiles(tenantId);
  86 + }
  87 +
  88 + @Test
  89 + public void testSaveOtaPackageWithMaxSumDataSizeOutOfLimit() {
  90 + TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId);
  91 + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxOtaPackagesInBytes(DATA_SIZE).build());
  92 + tenantProfileService.saveTenantProfile(tenantId, defaultTenantProfile);
  93 +
  94 + Assert.assertEquals(0, otaPackageService.sumDataSizeByTenantId(tenantId));
  95 +
  96 + createFirmware(tenantId, "1");
  97 + Assert.assertEquals(1, otaPackageService.sumDataSizeByTenantId(tenantId));
  98 +
  99 + thrown.expect(DataValidationException.class);
  100 + thrown.expectMessage(String.format("Failed to create the ota package, files size limit is exhausted %d bytes!", DATA_SIZE));
  101 + createFirmware(tenantId, "2");
  102 + }
  103 +
  104 + @Test
  105 + public void sumDataSizeByTenantId() {
  106 + Assert.assertEquals(0, otaPackageService.sumDataSizeByTenantId(tenantId));
  107 +
  108 + createFirmware(tenantId, "0.1");
  109 + Assert.assertEquals(1, otaPackageService.sumDataSizeByTenantId(tenantId));
  110 +
  111 + int maxSumDataSize = 8;
  112 + List<OtaPackage> packages = new ArrayList<>(maxSumDataSize);
  113 +
  114 + for (int i = 2; i <= maxSumDataSize; i++) {
  115 + packages.add(createFirmware(tenantId, "0." + i));
  116 + Assert.assertEquals(i, otaPackageService.sumDataSizeByTenantId(tenantId));
  117 + }
  118 +
  119 + Assert.assertEquals(maxSumDataSize, otaPackageService.sumDataSizeByTenantId(tenantId));
81 } 120 }
82 121
83 @Test 122 @Test
@@ -93,6 +132,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -93,6 +132,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
93 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 132 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
94 firmware.setChecksum(CHECKSUM); 133 firmware.setChecksum(CHECKSUM);
95 firmware.setData(DATA); 134 firmware.setData(DATA);
  135 + firmware.setDataSize(DATA_SIZE);
96 OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 136 OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware);
97 137
98 Assert.assertNotNull(savedFirmware); 138 Assert.assertNotNull(savedFirmware);
@@ -114,6 +154,35 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -114,6 +154,35 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
114 } 154 }
115 155
116 @Test 156 @Test
  157 + public void testSaveFirmwareWithUrl() {
  158 + OtaPackageInfo firmware = new OtaPackageInfo();
  159 + firmware.setTenantId(tenantId);
  160 + firmware.setDeviceProfileId(deviceProfileId);
  161 + firmware.setType(FIRMWARE);
  162 + firmware.setTitle(TITLE);
  163 + firmware.setVersion(VERSION);
  164 + firmware.setUrl(URL);
  165 + firmware.setDataSize(0L);
  166 + OtaPackageInfo savedFirmware = otaPackageService.saveOtaPackageInfo(firmware);
  167 +
  168 + Assert.assertNotNull(savedFirmware);
  169 + Assert.assertNotNull(savedFirmware.getId());
  170 + Assert.assertTrue(savedFirmware.getCreatedTime() > 0);
  171 + Assert.assertEquals(firmware.getTenantId(), savedFirmware.getTenantId());
  172 + Assert.assertEquals(firmware.getTitle(), savedFirmware.getTitle());
  173 + Assert.assertEquals(firmware.getFileName(), savedFirmware.getFileName());
  174 + Assert.assertEquals(firmware.getContentType(), savedFirmware.getContentType());
  175 +
  176 + savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode());
  177 + otaPackageService.saveOtaPackageInfo(savedFirmware);
  178 +
  179 + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId());
  180 + Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle());
  181 +
  182 + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId());
  183 + }
  184 +
  185 + @Test
117 public void testSaveFirmwareInfoAndUpdateWithData() { 186 public void testSaveFirmwareInfoAndUpdateWithData() {
118 OtaPackageInfo firmwareInfo = new OtaPackageInfo(); 187 OtaPackageInfo firmwareInfo = new OtaPackageInfo();
119 firmwareInfo.setTenantId(tenantId); 188 firmwareInfo.setTenantId(tenantId);
@@ -141,6 +210,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -141,6 +210,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
141 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 210 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
142 firmware.setChecksum(CHECKSUM); 211 firmware.setChecksum(CHECKSUM);
143 firmware.setData(DATA); 212 firmware.setData(DATA);
  213 + firmware.setDataSize(DATA_SIZE);
144 214
145 otaPackageService.saveOtaPackage(firmware); 215 otaPackageService.saveOtaPackage(firmware);
146 216
@@ -345,50 +415,15 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -345,50 +415,15 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
345 415
346 @Test 416 @Test
347 public void testSaveFirmwareWithExistingTitleAndVersion() { 417 public void testSaveFirmwareWithExistingTitleAndVersion() {
348 - OtaPackage firmware = new OtaPackage();  
349 - firmware.setTenantId(tenantId);  
350 - firmware.setDeviceProfileId(deviceProfileId);  
351 - firmware.setType(FIRMWARE);  
352 - firmware.setTitle(TITLE);  
353 - firmware.setVersion(VERSION);  
354 - firmware.setFileName(FILE_NAME);  
355 - firmware.setContentType(CONTENT_TYPE);  
356 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
357 - firmware.setChecksum(CHECKSUM);  
358 - firmware.setData(DATA);  
359 - otaPackageService.saveOtaPackage(firmware);  
360 -  
361 - OtaPackage newFirmware = new OtaPackage();  
362 - newFirmware.setTenantId(tenantId);  
363 - newFirmware.setDeviceProfileId(deviceProfileId);  
364 - newFirmware.setType(FIRMWARE);  
365 - newFirmware.setTitle(TITLE);  
366 - newFirmware.setVersion(VERSION);  
367 - newFirmware.setFileName(FILE_NAME);  
368 - newFirmware.setContentType(CONTENT_TYPE);  
369 - newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
370 - newFirmware.setChecksum(CHECKSUM);  
371 - newFirmware.setData(DATA);  
372 - 418 + createFirmware(tenantId, VERSION);
373 thrown.expect(DataValidationException.class); 419 thrown.expect(DataValidationException.class);
374 thrown.expectMessage("OtaPackage with such title and version already exists!"); 420 thrown.expectMessage("OtaPackage with such title and version already exists!");
375 - otaPackageService.saveOtaPackage(newFirmware); 421 + createFirmware(tenantId, VERSION);
376 } 422 }
377 423
378 @Test 424 @Test
379 public void testDeleteFirmwareWithReferenceByDevice() { 425 public void testDeleteFirmwareWithReferenceByDevice() {
380 - OtaPackage firmware = new OtaPackage();  
381 - firmware.setTenantId(tenantId);  
382 - firmware.setDeviceProfileId(deviceProfileId);  
383 - firmware.setType(FIRMWARE);  
384 - firmware.setTitle(TITLE);  
385 - firmware.setVersion(VERSION);  
386 - firmware.setFileName(FILE_NAME);  
387 - firmware.setContentType(CONTENT_TYPE);  
388 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
389 - firmware.setChecksum(CHECKSUM);  
390 - firmware.setData(DATA);  
391 - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 426 + OtaPackage savedFirmware = createFirmware(tenantId, VERSION);
392 427
393 Device device = new Device(); 428 Device device = new Device();
394 device.setTenantId(tenantId); 429 device.setTenantId(tenantId);
@@ -409,18 +444,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -409,18 +444,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
409 444
410 @Test 445 @Test
411 public void testUpdateDeviceProfileId() { 446 public void testUpdateDeviceProfileId() {
412 - OtaPackage firmware = new OtaPackage();  
413 - firmware.setTenantId(tenantId);  
414 - firmware.setDeviceProfileId(deviceProfileId);  
415 - firmware.setType(FIRMWARE);  
416 - firmware.setTitle(TITLE);  
417 - firmware.setVersion(VERSION);  
418 - firmware.setFileName(FILE_NAME);  
419 - firmware.setContentType(CONTENT_TYPE);  
420 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
421 - firmware.setChecksum(CHECKSUM);  
422 - firmware.setData(DATA);  
423 - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 447 + OtaPackage savedFirmware = createFirmware(tenantId, VERSION);
424 448
425 try { 449 try {
426 thrown.expect(DataValidationException.class); 450 thrown.expect(DataValidationException.class);
@@ -448,6 +472,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -448,6 +472,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
448 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); 472 firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
449 firmware.setChecksum(CHECKSUM); 473 firmware.setChecksum(CHECKSUM);
450 firmware.setData(DATA); 474 firmware.setData(DATA);
  475 + firmware.setDataSize(DATA_SIZE);
451 OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 476 OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware);
452 477
453 savedDeviceProfile.setFirmwareId(savedFirmware.getId()); 478 savedDeviceProfile.setFirmwareId(savedFirmware.getId());
@@ -465,18 +490,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -465,18 +490,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
465 490
466 @Test 491 @Test
467 public void testFindFirmwareById() { 492 public void testFindFirmwareById() {
468 - OtaPackage firmware = new OtaPackage();  
469 - firmware.setTenantId(tenantId);  
470 - firmware.setDeviceProfileId(deviceProfileId);  
471 - firmware.setType(FIRMWARE);  
472 - firmware.setTitle(TITLE);  
473 - firmware.setVersion(VERSION);  
474 - firmware.setFileName(FILE_NAME);  
475 - firmware.setContentType(CONTENT_TYPE);  
476 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
477 - firmware.setChecksum(CHECKSUM);  
478 - firmware.setData(DATA);  
479 - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 493 + OtaPackage savedFirmware = createFirmware(tenantId, VERSION);
480 494
481 OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); 495 OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId());
482 Assert.assertNotNull(foundFirmware); 496 Assert.assertNotNull(foundFirmware);
@@ -502,18 +516,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -502,18 +516,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
502 516
503 @Test 517 @Test
504 public void testDeleteFirmware() { 518 public void testDeleteFirmware() {
505 - OtaPackage firmware = new OtaPackage();  
506 - firmware.setTenantId(tenantId);  
507 - firmware.setDeviceProfileId(deviceProfileId);  
508 - firmware.setType(FIRMWARE);  
509 - firmware.setTitle(TITLE);  
510 - firmware.setVersion(VERSION);  
511 - firmware.setFileName(FILE_NAME);  
512 - firmware.setContentType(CONTENT_TYPE);  
513 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
514 - firmware.setChecksum(CHECKSUM);  
515 - firmware.setData(DATA);  
516 - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); 519 + OtaPackage savedFirmware = createFirmware(tenantId, VERSION);
517 520
518 OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); 521 OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId());
519 Assert.assertNotNull(foundFirmware); 522 Assert.assertNotNull(foundFirmware);
@@ -526,23 +529,25 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -526,23 +529,25 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
526 public void testFindTenantFirmwaresByTenantId() { 529 public void testFindTenantFirmwaresByTenantId() {
527 List<OtaPackageInfo> firmwares = new ArrayList<>(); 530 List<OtaPackageInfo> firmwares = new ArrayList<>();
528 for (int i = 0; i < 165; i++) { 531 for (int i = 0; i < 165; i++) {
529 - OtaPackage firmware = new OtaPackage();  
530 - firmware.setTenantId(tenantId);  
531 - firmware.setDeviceProfileId(deviceProfileId);  
532 - firmware.setType(FIRMWARE);  
533 - firmware.setTitle(TITLE);  
534 - firmware.setVersion(VERSION + i);  
535 - firmware.setFileName(FILE_NAME);  
536 - firmware.setContentType(CONTENT_TYPE);  
537 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
538 - firmware.setChecksum(CHECKSUM);  
539 - firmware.setData(DATA);  
540 -  
541 - OtaPackageInfo info = new OtaPackageInfo(otaPackageService.saveOtaPackage(firmware)); 532 + OtaPackageInfo info = new OtaPackageInfo(createFirmware(tenantId, VERSION + i));
542 info.setHasData(true); 533 info.setHasData(true);
543 firmwares.add(info); 534 firmwares.add(info);
544 } 535 }
545 536
  537 + OtaPackageInfo firmwareWithUrl = new OtaPackageInfo();
  538 + firmwareWithUrl.setTenantId(tenantId);
  539 + firmwareWithUrl.setDeviceProfileId(deviceProfileId);
  540 + firmwareWithUrl.setType(FIRMWARE);
  541 + firmwareWithUrl.setTitle(TITLE);
  542 + firmwareWithUrl.setVersion(VERSION);
  543 + firmwareWithUrl.setUrl(URL);
  544 + firmwareWithUrl.setDataSize(0L);
  545 +
  546 + OtaPackageInfo savedFwWithUrl = otaPackageService.saveOtaPackageInfo(firmwareWithUrl);
  547 + savedFwWithUrl.setHasData(true);
  548 +
  549 + firmwares.add(savedFwWithUrl);
  550 +
546 List<OtaPackageInfo> loadedFirmwares = new ArrayList<>(); 551 List<OtaPackageInfo> loadedFirmwares = new ArrayList<>();
547 PageLink pageLink = new PageLink(16); 552 PageLink pageLink = new PageLink(16);
548 PageData<OtaPackageInfo> pageData; 553 PageData<OtaPackageInfo> pageData;
@@ -571,58 +576,38 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -571,58 +576,38 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
571 public void testFindTenantFirmwaresByTenantIdAndHasData() { 576 public void testFindTenantFirmwaresByTenantIdAndHasData() {
572 List<OtaPackageInfo> firmwares = new ArrayList<>(); 577 List<OtaPackageInfo> firmwares = new ArrayList<>();
573 for (int i = 0; i < 165; i++) { 578 for (int i = 0; i < 165; i++) {
574 - OtaPackageInfo firmwareInfo = new OtaPackageInfo();  
575 - firmwareInfo.setTenantId(tenantId);  
576 - firmwareInfo.setDeviceProfileId(deviceProfileId);  
577 - firmwareInfo.setType(FIRMWARE);  
578 - firmwareInfo.setTitle(TITLE);  
579 - firmwareInfo.setVersion(VERSION + i);  
580 - firmwareInfo.setFileName(FILE_NAME);  
581 - firmwareInfo.setContentType(CONTENT_TYPE);  
582 - firmwareInfo.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
583 - firmwareInfo.setChecksum(CHECKSUM);  
584 - firmwareInfo.setDataSize((long) DATA.array().length);  
585 - firmwares.add(otaPackageService.saveOtaPackageInfo(firmwareInfo)); 579 + firmwares.add(new OtaPackageInfo(otaPackageService.saveOtaPackage(createFirmware(tenantId, VERSION + i))));
586 } 580 }
587 581
  582 + OtaPackageInfo firmwareWithUrl = new OtaPackageInfo();
  583 + firmwareWithUrl.setTenantId(tenantId);
  584 + firmwareWithUrl.setDeviceProfileId(deviceProfileId);
  585 + firmwareWithUrl.setType(FIRMWARE);
  586 + firmwareWithUrl.setTitle(TITLE);
  587 + firmwareWithUrl.setVersion(VERSION);
  588 + firmwareWithUrl.setUrl(URL);
  589 + firmwareWithUrl.setDataSize(0L);
  590 +
  591 + OtaPackageInfo savedFwWithUrl = otaPackageService.saveOtaPackageInfo(firmwareWithUrl);
  592 + savedFwWithUrl.setHasData(true);
  593 +
  594 + firmwares.add(savedFwWithUrl);
  595 +
588 List<OtaPackageInfo> loadedFirmwares = new ArrayList<>(); 596 List<OtaPackageInfo> loadedFirmwares = new ArrayList<>();
589 PageLink pageLink = new PageLink(16); 597 PageLink pageLink = new PageLink(16);
590 PageData<OtaPackageInfo> pageData; 598 PageData<OtaPackageInfo> pageData;
591 do { 599 do {
592 - pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); 600 + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, pageLink);
593 loadedFirmwares.addAll(pageData.getData()); 601 loadedFirmwares.addAll(pageData.getData());
594 if (pageData.hasNext()) { 602 if (pageData.hasNext()) {
595 pageLink = pageLink.nextPageLink(); 603 pageLink = pageLink.nextPageLink();
596 } 604 }
597 } while (pageData.hasNext()); 605 } while (pageData.hasNext());
598 606
599 - Collections.sort(firmwares, idComparator);  
600 - Collections.sort(loadedFirmwares, idComparator);  
601 -  
602 - Assert.assertEquals(firmwares, loadedFirmwares);  
603 -  
604 - firmwares.forEach(f -> {  
605 - OtaPackage firmware = new OtaPackage(f.getId());  
606 - firmware.setCreatedTime(f.getCreatedTime());  
607 - firmware.setTenantId(f.getTenantId());  
608 - firmware.setDeviceProfileId(deviceProfileId);  
609 - firmware.setType(FIRMWARE);  
610 - firmware.setTitle(f.getTitle());  
611 - firmware.setVersion(f.getVersion());  
612 - firmware.setFileName(FILE_NAME);  
613 - firmware.setContentType(CONTENT_TYPE);  
614 - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);  
615 - firmware.setChecksum(CHECKSUM);  
616 - firmware.setData(DATA);  
617 - firmware.setDataSize((long) DATA.array().length);  
618 - otaPackageService.saveOtaPackage(firmware);  
619 - f.setHasData(true);  
620 - });  
621 -  
622 loadedFirmwares = new ArrayList<>(); 607 loadedFirmwares = new ArrayList<>();
623 pageLink = new PageLink(16); 608 pageLink = new PageLink(16);
624 do { 609 do {
625 - pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); 610 + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, pageLink);
626 loadedFirmwares.addAll(pageData.getData()); 611 loadedFirmwares.addAll(pageData.getData());
627 if (pageData.hasNext()) { 612 if (pageData.hasNext()) {
628 pageLink = pageLink.nextPageLink(); 613 pageLink = pageLink.nextPageLink();
@@ -642,4 +627,20 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @@ -642,4 +627,20 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest {
642 Assert.assertTrue(pageData.getData().isEmpty()); 627 Assert.assertTrue(pageData.getData().isEmpty());
643 } 628 }
644 629
  630 + private OtaPackage createFirmware(TenantId tenantId, String version) {
  631 + OtaPackage firmware = new OtaPackage();
  632 + firmware.setTenantId(tenantId);
  633 + firmware.setDeviceProfileId(deviceProfileId);
  634 + firmware.setType(FIRMWARE);
  635 + firmware.setTitle(TITLE);
  636 + firmware.setVersion(version);
  637 + firmware.setFileName(FILE_NAME);
  638 + firmware.setContentType(CONTENT_TYPE);
  639 + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM);
  640 + firmware.setChecksum(CHECKSUM);
  641 + firmware.setData(DATA);
  642 + firmware.setDataSize(DATA_SIZE);
  643 + return otaPackageService.saveOtaPackage(firmware);
  644 + }
  645 +
645 } 646 }
@@ -47,7 +47,9 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -47,7 +47,9 @@ import org.thingsboard.server.dao.edge.EdgeService;
47 import org.thingsboard.server.dao.entityview.EntityViewService; 47 import org.thingsboard.server.dao.entityview.EntityViewService;
48 import org.thingsboard.server.dao.nosql.CassandraStatementTask; 48 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
49 import org.thingsboard.server.dao.nosql.TbResultSetFuture; 49 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
  50 +import org.thingsboard.server.dao.ota.OtaPackageService;
50 import org.thingsboard.server.dao.relation.RelationService; 51 import org.thingsboard.server.dao.relation.RelationService;
  52 +import org.thingsboard.server.dao.resource.ResourceService;
51 import org.thingsboard.server.dao.rule.RuleChainService; 53 import org.thingsboard.server.dao.rule.RuleChainService;
52 import org.thingsboard.server.dao.tenant.TenantService; 54 import org.thingsboard.server.dao.tenant.TenantService;
53 import org.thingsboard.server.dao.timeseries.TimeseriesService; 55 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -202,6 +204,10 @@ public interface TbContext { @@ -202,6 +204,10 @@ public interface TbContext {
202 204
203 EntityViewService getEntityViewService(); 205 EntityViewService getEntityViewService();
204 206
  207 + ResourceService getResourceService();
  208 +
  209 + OtaPackageService getOtaPackageService();
  210 +
205 RuleEngineDeviceProfileCache getDeviceProfileCache(); 211 RuleEngineDeviceProfileCache getDeviceProfileCache();
206 212
207 EdgeService getEdgeService(); 213 EdgeService getEdgeService();
@@ -40,7 +40,7 @@ export class OtaPackageService { @@ -40,7 +40,7 @@ export class OtaPackageService {
40 40
41 public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, 41 public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType,
42 hasData = true, config?: RequestConfig): Observable<PageData<OtaPackageInfo>> { 42 hasData = true, config?: RequestConfig): Observable<PageData<OtaPackageInfo>> {
43 - const url = `/api/otaPackages/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; 43 + const url = `/api/otaPackages/${deviceProfileId}/${type}${pageLink.toQuery()}`;
44 return this.http.get<PageData<OtaPackageInfo>>(url, defaultHttpOptionsFromConfig(config)); 44 return this.http.get<PageData<OtaPackageInfo>>(url, defaultHttpOptionsFromConfig(config));
45 } 45 }
46 46
@@ -89,6 +89,30 @@ @@ -89,6 +89,30 @@
89 </mat-error> 89 </mat-error>
90 </mat-form-field> 90 </mat-form-field>
91 <mat-form-field class="mat-block"> 91 <mat-form-field class="mat-block">
  92 + <mat-label translate>tenant-profile.maximum-resources-sum-data-size</mat-label>
  93 + <input matInput required min="0" step="1"
  94 + formControlName="maxResourcesInBytes"
  95 + type="number">
  96 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxResourcesInBytes').hasError('required')">
  97 + {{ 'tenant-profile.maximum-resources-sum-data-size-required' | translate}}
  98 + </mat-error>
  99 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxResourcesInBytes').hasError('min')">
  100 + {{ 'tenant-profile.maximum-resources-sum-data-size-range' | translate}}
  101 + </mat-error>
  102 + </mat-form-field>
  103 + <mat-form-field class="mat-block">
  104 + <mat-label translate>tenant-profile.maximum-ota-packages-sum-data-size</mat-label>
  105 + <input matInput required min="0" step="1"
  106 + formControlName="maxOtaPackagesInBytes"
  107 + type="number">
  108 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxOtaPackagesInBytes').hasError('required')">
  109 + {{ 'tenant-profile.maximum-ota-packages-sum-data-size-required' | translate}}
  110 + </mat-error>
  111 + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxOtaPackagesInBytes').hasError('min')">
  112 + {{ 'tenant-profile.maximum-ota-packages-sum-data-size-range' | translate}}
  113 + </mat-error>
  114 + </mat-form-field>
  115 + <mat-form-field class="mat-block">
92 <mat-label translate>tenant-profile.max-transport-messages</mat-label> 116 <mat-label translate>tenant-profile.max-transport-messages</mat-label>
93 <input matInput required min="0" step="1" 117 <input matInput required min="0" step="1"
94 formControlName="maxTransportMessages" 118 formControlName="maxTransportMessages"
@@ -59,6 +59,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA @@ -59,6 +59,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
59 maxUsers: [null, [Validators.required, Validators.min(0)]], 59 maxUsers: [null, [Validators.required, Validators.min(0)]],
60 maxDashboards: [null, [Validators.required, Validators.min(0)]], 60 maxDashboards: [null, [Validators.required, Validators.min(0)]],
61 maxRuleChains: [null, [Validators.required, Validators.min(0)]], 61 maxRuleChains: [null, [Validators.required, Validators.min(0)]],
  62 + maxResourcesInBytes: [null, [Validators.required, Validators.min(0)]],
  63 + maxOtaPackagesInBytes: [null, [Validators.required, Validators.min(0)]],
62 transportTenantMsgRateLimit: [null, []], 64 transportTenantMsgRateLimit: [null, []],
63 transportTenantTelemetryMsgRateLimit: [null, []], 65 transportTenantTelemetryMsgRateLimit: [null, []],
64 transportTenantTelemetryDataPointsRateLimit: [null, []], 66 transportTenantTelemetryDataPointsRateLimit: [null, []],
@@ -18,6 +18,7 @@ import { ContactBased } from '@shared/models/contact-based.model'; @@ -18,6 +18,7 @@ import { ContactBased } from '@shared/models/contact-based.model';
18 import { TenantId } from './id/tenant-id'; 18 import { TenantId } from './id/tenant-id';
19 import { TenantProfileId } from '@shared/models/id/tenant-profile-id'; 19 import { TenantProfileId } from '@shared/models/id/tenant-profile-id';
20 import { BaseData } from '@shared/models/base-data'; 20 import { BaseData } from '@shared/models/base-data';
  21 +import {Validators} from "@angular/forms";
21 22
22 export enum TenantProfileType { 23 export enum TenantProfileType {
23 DEFAULT = 'DEFAULT' 24 DEFAULT = 'DEFAULT'
@@ -30,6 +31,8 @@ export interface DefaultTenantProfileConfiguration { @@ -30,6 +31,8 @@ export interface DefaultTenantProfileConfiguration {
30 maxUsers: number; 31 maxUsers: number;
31 maxDashboards: number; 32 maxDashboards: number;
32 maxRuleChains: number; 33 maxRuleChains: number;
  34 + maxResourcesInBytes: number;
  35 + maxOtaPackagesInBytes: number;
33 36
34 transportTenantMsgRateLimit?: string; 37 transportTenantMsgRateLimit?: string;
35 transportTenantTelemetryMsgRateLimit?: string; 38 transportTenantTelemetryMsgRateLimit?: string;
@@ -68,6 +71,8 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan @@ -68,6 +71,8 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan
68 maxUsers: 0, 71 maxUsers: 0,
69 maxDashboards: 0, 72 maxDashboards: 0,
70 maxRuleChains: 0, 73 maxRuleChains: 0,
  74 + maxResourcesInBytes: 0,
  75 + maxOtaPackagesInBytes: 0,
71 maxTransportMessages: 0, 76 maxTransportMessages: 0,
72 maxTransportDataPoints: 0, 77 maxTransportDataPoints: 0,
73 maxREExecutions: 0, 78 maxREExecutions: 0,
@@ -2497,6 +2497,12 @@ @@ -2497,6 +2497,12 @@
2497 "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)", 2497 "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)",
2498 "maximum-rule-chains-required": "Maximum number of rule chains is required.", 2498 "maximum-rule-chains-required": "Maximum number of rule chains is required.",
2499 "maximum-rule-chains-range": "Maximum number of rule chains can't be negative", 2499 "maximum-rule-chains-range": "Maximum number of rule chains can't be negative",
  2500 + "maximum-resources-sum-data-size": "Maximum sum of resource files size in bytes (0 - unlimited)",
  2501 + "maximum-resources-sum-data-size-required": "Maximum sum of resource files size is required.",
  2502 + "maximum-resources-sum-data-size-range": "Maximum sum of resource files size can`t be negative",
  2503 + "maximum-ota-packages-sum-data-size": "Maximum sum of ota package files size in bytes (0 - unlimited)",
  2504 + "maximum-ota-package-sum-data-size-required": "Maximum sum of ota package files size is required.",
  2505 + "maximum-ota-package-sum-data-size-range": "Maximum sum of ota package files size can`t be negative",
2500 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", 2506 "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.",
2501 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", 2507 "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.",
2502 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", 2508 "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.",