Commit 6b63d336dd58c3707ae7a704cc5ba86d8c056aee
Merge remote-tracking branch 'upstream/master' into improvement/ota-updates/select
Showing
80 changed files
with
1450 additions
and
432 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 | } |
@@ -110,8 +110,11 @@ public class AlarmController extends BaseController { | @@ -110,8 +110,11 @@ public class AlarmController extends BaseController { | ||
110 | checkParameter(ALARM_ID, strAlarmId); | 110 | checkParameter(ALARM_ID, strAlarmId); |
111 | try { | 111 | try { |
112 | AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); | 112 | AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); |
113 | - checkAlarmId(alarmId, Operation.WRITE); | 113 | + Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); |
114 | 114 | ||
115 | + logEntityAction(alarm.getOriginator(), alarm, | ||
116 | + getCurrentUser().getCustomerId(), | ||
117 | + ActionType.ALARM_DELETE, null); | ||
115 | sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED); | 118 | sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED); |
116 | 119 | ||
117 | return alarmService.deleteAlarm(getTenantId(), alarmId); | 120 | return alarmService.deleteAlarm(getTenantId(), alarmId); |
@@ -17,7 +17,6 @@ package org.thingsboard.server.controller; | @@ -17,7 +17,6 @@ package org.thingsboard.server.controller; | ||
17 | 17 | ||
18 | import com.fasterxml.jackson.core.JsonProcessingException; | 18 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | import com.fasterxml.jackson.databind.ObjectMapper; | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
20 | -import com.fasterxml.jackson.databind.node.ArrayNode; | ||
21 | import com.fasterxml.jackson.databind.node.ObjectNode; | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
22 | import lombok.Getter; | 21 | import lombok.Getter; |
23 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
@@ -31,7 +30,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; | @@ -31,7 +30,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; | ||
31 | import org.thingsboard.server.common.data.Customer; | 30 | import org.thingsboard.server.common.data.Customer; |
32 | import org.thingsboard.server.common.data.Dashboard; | 31 | import org.thingsboard.server.common.data.Dashboard; |
33 | import org.thingsboard.server.common.data.DashboardInfo; | 32 | import org.thingsboard.server.common.data.DashboardInfo; |
34 | -import org.thingsboard.server.common.data.DataConstants; | ||
35 | import org.thingsboard.server.common.data.Device; | 33 | import org.thingsboard.server.common.data.Device; |
36 | import org.thingsboard.server.common.data.DeviceInfo; | 34 | import org.thingsboard.server.common.data.DeviceInfo; |
37 | import org.thingsboard.server.common.data.DeviceProfile; | 35 | import org.thingsboard.server.common.data.DeviceProfile; |
@@ -79,10 +77,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId; | @@ -79,10 +77,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId; | ||
79 | import org.thingsboard.server.common.data.id.UserId; | 77 | import org.thingsboard.server.common.data.id.UserId; |
80 | import org.thingsboard.server.common.data.id.WidgetTypeId; | 78 | import org.thingsboard.server.common.data.id.WidgetTypeId; |
81 | import org.thingsboard.server.common.data.id.WidgetsBundleId; | 79 | import org.thingsboard.server.common.data.id.WidgetsBundleId; |
82 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
83 | -import org.thingsboard.server.common.data.kv.DataType; | ||
84 | -import org.thingsboard.server.common.data.kv.KvEntry; | ||
85 | -import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
86 | import org.thingsboard.server.common.data.page.PageLink; | 80 | import org.thingsboard.server.common.data.page.PageLink; |
87 | import org.thingsboard.server.common.data.page.SortOrder; | 81 | import org.thingsboard.server.common.data.page.SortOrder; |
88 | import org.thingsboard.server.common.data.page.TimePageLink; | 82 | import org.thingsboard.server.common.data.page.TimePageLink; |
@@ -94,9 +88,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType; | @@ -94,9 +88,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType; | ||
94 | import org.thingsboard.server.common.data.rule.RuleNode; | 88 | import org.thingsboard.server.common.data.rule.RuleNode; |
95 | import org.thingsboard.server.common.data.widget.WidgetTypeDetails; | 89 | import org.thingsboard.server.common.data.widget.WidgetTypeDetails; |
96 | import org.thingsboard.server.common.data.widget.WidgetsBundle; | 90 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
97 | -import org.thingsboard.server.common.msg.TbMsg; | ||
98 | -import org.thingsboard.server.common.msg.TbMsgDataType; | ||
99 | -import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
100 | import org.thingsboard.server.dao.asset.AssetService; | 91 | import org.thingsboard.server.dao.asset.AssetService; |
101 | import org.thingsboard.server.dao.attributes.AttributesService; | 92 | import org.thingsboard.server.dao.attributes.AttributesService; |
102 | import org.thingsboard.server.dao.audit.AuditLogService; | 93 | import org.thingsboard.server.dao.audit.AuditLogService; |
@@ -127,6 +118,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; | @@ -127,6 +118,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; | ||
127 | import org.thingsboard.server.queue.discovery.PartitionService; | 118 | import org.thingsboard.server.queue.discovery.PartitionService; |
128 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; | 119 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
129 | import org.thingsboard.server.queue.util.TbCoreComponent; | 120 | import org.thingsboard.server.queue.util.TbCoreComponent; |
121 | +import org.thingsboard.server.service.action.RuleEngineEntityActionService; | ||
130 | import org.thingsboard.server.service.component.ComponentDiscoveryService; | 122 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
131 | import org.thingsboard.server.service.ota.OtaPackageStateService; | 123 | import org.thingsboard.server.service.ota.OtaPackageStateService; |
132 | import org.thingsboard.server.service.edge.EdgeNotificationService; | 124 | import org.thingsboard.server.service.edge.EdgeNotificationService; |
@@ -147,11 +139,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | @@ -147,11 +139,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | ||
147 | import javax.mail.MessagingException; | 139 | import javax.mail.MessagingException; |
148 | import javax.servlet.http.HttpServletResponse; | 140 | import javax.servlet.http.HttpServletResponse; |
149 | import java.util.List; | 141 | import java.util.List; |
150 | -import java.util.Map; | ||
151 | import java.util.Optional; | 142 | import java.util.Optional; |
152 | import java.util.Set; | 143 | import java.util.Set; |
153 | import java.util.UUID; | 144 | import java.util.UUID; |
154 | -import java.util.stream.Collectors; | ||
155 | 145 | ||
156 | import static org.thingsboard.server.dao.service.Validator.validateId; | 146 | import static org.thingsboard.server.dao.service.Validator.validateId; |
157 | 147 | ||
@@ -279,6 +269,9 @@ public abstract class BaseController { | @@ -279,6 +269,9 @@ public abstract class BaseController { | ||
279 | @Autowired(required = false) | 269 | @Autowired(required = false) |
280 | protected EdgeGrpcService edgeGrpcService; | 270 | protected EdgeGrpcService edgeGrpcService; |
281 | 271 | ||
272 | + @Autowired | ||
273 | + protected RuleEngineEntityActionService ruleEngineEntityActionService; | ||
274 | + | ||
282 | @Value("${server.log_controller_error_stack_trace}") | 275 | @Value("${server.log_controller_error_stack_trace}") |
283 | @Getter | 276 | @Getter |
284 | private boolean logControllerErrorStackTrace; | 277 | private boolean logControllerErrorStackTrace; |
@@ -809,7 +802,7 @@ public abstract class BaseController { | @@ -809,7 +802,7 @@ public abstract class BaseController { | ||
809 | customerId = user.getCustomerId(); | 802 | customerId = user.getCustomerId(); |
810 | } | 803 | } |
811 | if (e == null) { | 804 | if (e == null) { |
812 | - pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); | 805 | + ruleEngineEntityActionService.pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo); |
813 | } | 806 | } |
814 | auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); | 807 | auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); |
815 | } | 808 | } |
@@ -819,184 +812,6 @@ public abstract class BaseController { | @@ -819,184 +812,6 @@ public abstract class BaseController { | ||
819 | return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; | 812 | return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; |
820 | } | 813 | } |
821 | 814 | ||
822 | - private <E extends HasName, I extends EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, | ||
823 | - ActionType actionType, Object... additionalInfo) { | ||
824 | - String msgType = null; | ||
825 | - switch (actionType) { | ||
826 | - case ADDED: | ||
827 | - msgType = DataConstants.ENTITY_CREATED; | ||
828 | - break; | ||
829 | - case DELETED: | ||
830 | - msgType = DataConstants.ENTITY_DELETED; | ||
831 | - break; | ||
832 | - case UPDATED: | ||
833 | - msgType = DataConstants.ENTITY_UPDATED; | ||
834 | - break; | ||
835 | - case ASSIGNED_TO_CUSTOMER: | ||
836 | - msgType = DataConstants.ENTITY_ASSIGNED; | ||
837 | - break; | ||
838 | - case UNASSIGNED_FROM_CUSTOMER: | ||
839 | - msgType = DataConstants.ENTITY_UNASSIGNED; | ||
840 | - break; | ||
841 | - case ATTRIBUTES_UPDATED: | ||
842 | - msgType = DataConstants.ATTRIBUTES_UPDATED; | ||
843 | - break; | ||
844 | - case ATTRIBUTES_DELETED: | ||
845 | - msgType = DataConstants.ATTRIBUTES_DELETED; | ||
846 | - break; | ||
847 | - case ALARM_ACK: | ||
848 | - msgType = DataConstants.ALARM_ACK; | ||
849 | - break; | ||
850 | - case ALARM_CLEAR: | ||
851 | - msgType = DataConstants.ALARM_CLEAR; | ||
852 | - break; | ||
853 | - case ASSIGNED_FROM_TENANT: | ||
854 | - msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; | ||
855 | - break; | ||
856 | - case ASSIGNED_TO_TENANT: | ||
857 | - msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; | ||
858 | - break; | ||
859 | - case PROVISION_SUCCESS: | ||
860 | - msgType = DataConstants.PROVISION_SUCCESS; | ||
861 | - break; | ||
862 | - case PROVISION_FAILURE: | ||
863 | - msgType = DataConstants.PROVISION_FAILURE; | ||
864 | - break; | ||
865 | - case TIMESERIES_UPDATED: | ||
866 | - msgType = DataConstants.TIMESERIES_UPDATED; | ||
867 | - break; | ||
868 | - case TIMESERIES_DELETED: | ||
869 | - msgType = DataConstants.TIMESERIES_DELETED; | ||
870 | - break; | ||
871 | - case ASSIGNED_TO_EDGE: | ||
872 | - msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; | ||
873 | - break; | ||
874 | - case UNASSIGNED_FROM_EDGE: | ||
875 | - msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; | ||
876 | - break; | ||
877 | - } | ||
878 | - if (!StringUtils.isEmpty(msgType)) { | ||
879 | - try { | ||
880 | - TbMsgMetaData metaData = new TbMsgMetaData(); | ||
881 | - metaData.putValue("userId", user.getId().toString()); | ||
882 | - metaData.putValue("userName", user.getName()); | ||
883 | - if (customerId != null && !customerId.isNullUid()) { | ||
884 | - metaData.putValue("customerId", customerId.toString()); | ||
885 | - } | ||
886 | - if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { | ||
887 | - String strCustomerId = extractParameter(String.class, 1, additionalInfo); | ||
888 | - String strCustomerName = extractParameter(String.class, 2, additionalInfo); | ||
889 | - metaData.putValue("assignedCustomerId", strCustomerId); | ||
890 | - metaData.putValue("assignedCustomerName", strCustomerName); | ||
891 | - } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { | ||
892 | - String strCustomerId = extractParameter(String.class, 1, additionalInfo); | ||
893 | - String strCustomerName = extractParameter(String.class, 2, additionalInfo); | ||
894 | - metaData.putValue("unassignedCustomerId", strCustomerId); | ||
895 | - metaData.putValue("unassignedCustomerName", strCustomerName); | ||
896 | - } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { | ||
897 | - String strTenantId = extractParameter(String.class, 0, additionalInfo); | ||
898 | - String strTenantName = extractParameter(String.class, 1, additionalInfo); | ||
899 | - metaData.putValue("assignedFromTenantId", strTenantId); | ||
900 | - metaData.putValue("assignedFromTenantName", strTenantName); | ||
901 | - } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { | ||
902 | - String strTenantId = extractParameter(String.class, 0, additionalInfo); | ||
903 | - String strTenantName = extractParameter(String.class, 1, additionalInfo); | ||
904 | - metaData.putValue("assignedToTenantId", strTenantId); | ||
905 | - metaData.putValue("assignedToTenantName", strTenantName); | ||
906 | - } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { | ||
907 | - String strEdgeId = extractParameter(String.class, 1, additionalInfo); | ||
908 | - String strEdgeName = extractParameter(String.class, 2, additionalInfo); | ||
909 | - metaData.putValue("assignedEdgeId", strEdgeId); | ||
910 | - metaData.putValue("assignedEdgeName", strEdgeName); | ||
911 | - } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { | ||
912 | - String strEdgeId = extractParameter(String.class, 1, additionalInfo); | ||
913 | - String strEdgeName = extractParameter(String.class, 2, additionalInfo); | ||
914 | - metaData.putValue("unassignedEdgeId", strEdgeId); | ||
915 | - metaData.putValue("unassignedEdgeName", strEdgeName); | ||
916 | - } | ||
917 | - ObjectNode entityNode; | ||
918 | - if (entity != null) { | ||
919 | - entityNode = json.valueToTree(entity); | ||
920 | - if (entityId.getEntityType() == EntityType.DASHBOARD) { | ||
921 | - entityNode.put("configuration", ""); | ||
922 | - } | ||
923 | - } else { | ||
924 | - entityNode = json.createObjectNode(); | ||
925 | - if (actionType == ActionType.ATTRIBUTES_UPDATED) { | ||
926 | - String scope = extractParameter(String.class, 0, additionalInfo); | ||
927 | - @SuppressWarnings("unchecked") | ||
928 | - List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); | ||
929 | - metaData.putValue(DataConstants.SCOPE, scope); | ||
930 | - if (attributes != null) { | ||
931 | - for (AttributeKvEntry attr : attributes) { | ||
932 | - addKvEntry(entityNode, attr); | ||
933 | - } | ||
934 | - } | ||
935 | - } else if (actionType == ActionType.ATTRIBUTES_DELETED) { | ||
936 | - String scope = extractParameter(String.class, 0, additionalInfo); | ||
937 | - @SuppressWarnings("unchecked") | ||
938 | - List<String> keys = extractParameter(List.class, 1, additionalInfo); | ||
939 | - metaData.putValue(DataConstants.SCOPE, scope); | ||
940 | - ArrayNode attrsArrayNode = entityNode.putArray("attributes"); | ||
941 | - if (keys != null) { | ||
942 | - keys.forEach(attrsArrayNode::add); | ||
943 | - } | ||
944 | - } else if (actionType == ActionType.TIMESERIES_UPDATED) { | ||
945 | - @SuppressWarnings("unchecked") | ||
946 | - List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); | ||
947 | - addTimeseries(entityNode, timeseries); | ||
948 | - } else if (actionType == ActionType.TIMESERIES_DELETED) { | ||
949 | - @SuppressWarnings("unchecked") | ||
950 | - List<String> keys = extractParameter(List.class, 0, additionalInfo); | ||
951 | - if (keys != null) { | ||
952 | - ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); | ||
953 | - keys.forEach(timeseriesArrayNode::add); | ||
954 | - } | ||
955 | - entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); | ||
956 | - entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); | ||
957 | - } | ||
958 | - } | ||
959 | - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); | ||
960 | - TenantId tenantId = user.getTenantId(); | ||
961 | - if (tenantId.isNullUid()) { | ||
962 | - if (entity instanceof HasTenantId) { | ||
963 | - tenantId = ((HasTenantId) entity).getTenantId(); | ||
964 | - } | ||
965 | - } | ||
966 | - tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); | ||
967 | - } catch (Exception e) { | ||
968 | - log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | ||
969 | - } | ||
970 | - } | ||
971 | - } | ||
972 | - | ||
973 | - private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { | ||
974 | - if (kvEntry.getDataType() == DataType.BOOLEAN) { | ||
975 | - kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
976 | - } else if (kvEntry.getDataType() == DataType.DOUBLE) { | ||
977 | - kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
978 | - } else if (kvEntry.getDataType() == DataType.LONG) { | ||
979 | - kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
980 | - } else if (kvEntry.getDataType() == DataType.JSON) { | ||
981 | - if (kvEntry.getJsonValue().isPresent()) { | ||
982 | - entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); | ||
983 | - } | ||
984 | - } else { | ||
985 | - entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); | ||
986 | - } | ||
987 | - } | ||
988 | - | ||
989 | - private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { | ||
990 | - T result = null; | ||
991 | - if (additionalInfo != null && additionalInfo.length > index) { | ||
992 | - Object paramObject = additionalInfo[index]; | ||
993 | - if (clazz.isInstance(paramObject)) { | ||
994 | - result = clazz.cast(paramObject); | ||
995 | - } | ||
996 | - } | ||
997 | - return result; | ||
998 | - } | ||
999 | - | ||
1000 | protected <E extends HasName> String entityToStr(E entity) { | 815 | protected <E extends HasName> String entityToStr(E entity) { |
1001 | try { | 816 | try { |
1002 | return json.writeValueAsString(json.valueToTree(entity)); | 817 | return json.writeValueAsString(json.valueToTree(entity)); |
@@ -1093,23 +908,6 @@ public abstract class BaseController { | @@ -1093,23 +908,6 @@ public abstract class BaseController { | ||
1093 | return result; | 908 | return result; |
1094 | } | 909 | } |
1095 | 910 | ||
1096 | - private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { | ||
1097 | - if (timeseries != null && !timeseries.isEmpty()) { | ||
1098 | - ArrayNode result = entityNode.putArray("timeseries"); | ||
1099 | - Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream() | ||
1100 | - .collect(Collectors.groupingBy(TsKvEntry::getTs)); | ||
1101 | - for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) { | ||
1102 | - ObjectNode element = json.createObjectNode(); | ||
1103 | - element.put("ts", entry.getKey()); | ||
1104 | - ObjectNode values = element.putObject("values"); | ||
1105 | - for (TsKvEntry tsKvEntry : entry.getValue()) { | ||
1106 | - addKvEntry(values, tsKvEntry); | ||
1107 | - } | ||
1108 | - result.add(element); | ||
1109 | - } | ||
1110 | - } | ||
1111 | - } | ||
1112 | - | ||
1113 | protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { | 911 | protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { |
1114 | String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; | 912 | String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; |
1115 | if (dashboardId != null && !dashboardId.equals("null")) { | 913 | if (dashboardId != null && !dashboardId.equals("null")) { |
@@ -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 | } |
application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java
0 → 100644
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.service.action; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import lombok.RequiredArgsConstructor; | ||
22 | +import lombok.extern.slf4j.Slf4j; | ||
23 | +import org.apache.commons.lang3.StringUtils; | ||
24 | +import org.springframework.stereotype.Service; | ||
25 | +import org.thingsboard.server.common.data.DataConstants; | ||
26 | +import org.thingsboard.server.common.data.EntityType; | ||
27 | +import org.thingsboard.server.common.data.HasName; | ||
28 | +import org.thingsboard.server.common.data.HasTenantId; | ||
29 | +import org.thingsboard.server.common.data.User; | ||
30 | +import org.thingsboard.server.common.data.audit.ActionType; | ||
31 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
32 | +import org.thingsboard.server.common.data.id.EntityId; | ||
33 | +import org.thingsboard.server.common.data.id.TenantId; | ||
34 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
35 | +import org.thingsboard.server.common.data.kv.DataType; | ||
36 | +import org.thingsboard.server.common.data.kv.KvEntry; | ||
37 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
38 | +import org.thingsboard.server.common.msg.TbMsg; | ||
39 | +import org.thingsboard.server.common.msg.TbMsgDataType; | ||
40 | +import org.thingsboard.server.common.msg.TbMsgMetaData; | ||
41 | +import org.thingsboard.server.queue.util.TbCoreComponent; | ||
42 | +import org.thingsboard.server.service.queue.TbClusterService; | ||
43 | + | ||
44 | +import java.util.List; | ||
45 | +import java.util.Map; | ||
46 | +import java.util.stream.Collectors; | ||
47 | + | ||
48 | +@TbCoreComponent | ||
49 | +@Service | ||
50 | +@RequiredArgsConstructor | ||
51 | +@Slf4j | ||
52 | +public class RuleEngineEntityActionService { | ||
53 | + private final TbClusterService tbClusterService; | ||
54 | + | ||
55 | + private static final ObjectMapper json = new ObjectMapper(); | ||
56 | + | ||
57 | + public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, | ||
58 | + ActionType actionType, User user, Object... additionalInfo) { | ||
59 | + String msgType = null; | ||
60 | + switch (actionType) { | ||
61 | + case ADDED: | ||
62 | + msgType = DataConstants.ENTITY_CREATED; | ||
63 | + break; | ||
64 | + case DELETED: | ||
65 | + msgType = DataConstants.ENTITY_DELETED; | ||
66 | + break; | ||
67 | + case UPDATED: | ||
68 | + msgType = DataConstants.ENTITY_UPDATED; | ||
69 | + break; | ||
70 | + case ASSIGNED_TO_CUSTOMER: | ||
71 | + msgType = DataConstants.ENTITY_ASSIGNED; | ||
72 | + break; | ||
73 | + case UNASSIGNED_FROM_CUSTOMER: | ||
74 | + msgType = DataConstants.ENTITY_UNASSIGNED; | ||
75 | + break; | ||
76 | + case ATTRIBUTES_UPDATED: | ||
77 | + msgType = DataConstants.ATTRIBUTES_UPDATED; | ||
78 | + break; | ||
79 | + case ATTRIBUTES_DELETED: | ||
80 | + msgType = DataConstants.ATTRIBUTES_DELETED; | ||
81 | + break; | ||
82 | + case ALARM_ACK: | ||
83 | + msgType = DataConstants.ALARM_ACK; | ||
84 | + break; | ||
85 | + case ALARM_CLEAR: | ||
86 | + msgType = DataConstants.ALARM_CLEAR; | ||
87 | + break; | ||
88 | + case ALARM_DELETE: | ||
89 | + msgType = DataConstants.ALARM_DELETE; | ||
90 | + break; | ||
91 | + case ASSIGNED_FROM_TENANT: | ||
92 | + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; | ||
93 | + break; | ||
94 | + case ASSIGNED_TO_TENANT: | ||
95 | + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; | ||
96 | + break; | ||
97 | + case PROVISION_SUCCESS: | ||
98 | + msgType = DataConstants.PROVISION_SUCCESS; | ||
99 | + break; | ||
100 | + case PROVISION_FAILURE: | ||
101 | + msgType = DataConstants.PROVISION_FAILURE; | ||
102 | + break; | ||
103 | + case TIMESERIES_UPDATED: | ||
104 | + msgType = DataConstants.TIMESERIES_UPDATED; | ||
105 | + break; | ||
106 | + case TIMESERIES_DELETED: | ||
107 | + msgType = DataConstants.TIMESERIES_DELETED; | ||
108 | + break; | ||
109 | + case ASSIGNED_TO_EDGE: | ||
110 | + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; | ||
111 | + break; | ||
112 | + case UNASSIGNED_FROM_EDGE: | ||
113 | + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; | ||
114 | + break; | ||
115 | + } | ||
116 | + if (!StringUtils.isEmpty(msgType)) { | ||
117 | + try { | ||
118 | + TbMsgMetaData metaData = new TbMsgMetaData(); | ||
119 | + if (user != null) { | ||
120 | + metaData.putValue("userId", user.getId().toString()); | ||
121 | + metaData.putValue("userName", user.getName()); | ||
122 | + } | ||
123 | + if (customerId != null && !customerId.isNullUid()) { | ||
124 | + metaData.putValue("customerId", customerId.toString()); | ||
125 | + } | ||
126 | + if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { | ||
127 | + String strCustomerId = extractParameter(String.class, 1, additionalInfo); | ||
128 | + String strCustomerName = extractParameter(String.class, 2, additionalInfo); | ||
129 | + metaData.putValue("assignedCustomerId", strCustomerId); | ||
130 | + metaData.putValue("assignedCustomerName", strCustomerName); | ||
131 | + } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { | ||
132 | + String strCustomerId = extractParameter(String.class, 1, additionalInfo); | ||
133 | + String strCustomerName = extractParameter(String.class, 2, additionalInfo); | ||
134 | + metaData.putValue("unassignedCustomerId", strCustomerId); | ||
135 | + metaData.putValue("unassignedCustomerName", strCustomerName); | ||
136 | + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { | ||
137 | + String strTenantId = extractParameter(String.class, 0, additionalInfo); | ||
138 | + String strTenantName = extractParameter(String.class, 1, additionalInfo); | ||
139 | + metaData.putValue("assignedFromTenantId", strTenantId); | ||
140 | + metaData.putValue("assignedFromTenantName", strTenantName); | ||
141 | + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { | ||
142 | + String strTenantId = extractParameter(String.class, 0, additionalInfo); | ||
143 | + String strTenantName = extractParameter(String.class, 1, additionalInfo); | ||
144 | + metaData.putValue("assignedToTenantId", strTenantId); | ||
145 | + metaData.putValue("assignedToTenantName", strTenantName); | ||
146 | + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { | ||
147 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | ||
148 | + String strEdgeName = extractParameter(String.class, 2, additionalInfo); | ||
149 | + metaData.putValue("assignedEdgeId", strEdgeId); | ||
150 | + metaData.putValue("assignedEdgeName", strEdgeName); | ||
151 | + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { | ||
152 | + String strEdgeId = extractParameter(String.class, 1, additionalInfo); | ||
153 | + String strEdgeName = extractParameter(String.class, 2, additionalInfo); | ||
154 | + metaData.putValue("unassignedEdgeId", strEdgeId); | ||
155 | + metaData.putValue("unassignedEdgeName", strEdgeName); | ||
156 | + } | ||
157 | + ObjectNode entityNode; | ||
158 | + if (entity != null) { | ||
159 | + entityNode = json.valueToTree(entity); | ||
160 | + if (entityId.getEntityType() == EntityType.DASHBOARD) { | ||
161 | + entityNode.put("configuration", ""); | ||
162 | + } | ||
163 | + } else { | ||
164 | + entityNode = json.createObjectNode(); | ||
165 | + if (actionType == ActionType.ATTRIBUTES_UPDATED) { | ||
166 | + String scope = extractParameter(String.class, 0, additionalInfo); | ||
167 | + @SuppressWarnings("unchecked") | ||
168 | + List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo); | ||
169 | + metaData.putValue(DataConstants.SCOPE, scope); | ||
170 | + if (attributes != null) { | ||
171 | + for (AttributeKvEntry attr : attributes) { | ||
172 | + addKvEntry(entityNode, attr); | ||
173 | + } | ||
174 | + } | ||
175 | + } else if (actionType == ActionType.ATTRIBUTES_DELETED) { | ||
176 | + String scope = extractParameter(String.class, 0, additionalInfo); | ||
177 | + @SuppressWarnings("unchecked") | ||
178 | + List<String> keys = extractParameter(List.class, 1, additionalInfo); | ||
179 | + metaData.putValue(DataConstants.SCOPE, scope); | ||
180 | + ArrayNode attrsArrayNode = entityNode.putArray("attributes"); | ||
181 | + if (keys != null) { | ||
182 | + keys.forEach(attrsArrayNode::add); | ||
183 | + } | ||
184 | + } else if (actionType == ActionType.TIMESERIES_UPDATED) { | ||
185 | + @SuppressWarnings("unchecked") | ||
186 | + List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo); | ||
187 | + addTimeseries(entityNode, timeseries); | ||
188 | + } else if (actionType == ActionType.TIMESERIES_DELETED) { | ||
189 | + @SuppressWarnings("unchecked") | ||
190 | + List<String> keys = extractParameter(List.class, 0, additionalInfo); | ||
191 | + if (keys != null) { | ||
192 | + ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); | ||
193 | + keys.forEach(timeseriesArrayNode::add); | ||
194 | + } | ||
195 | + entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); | ||
196 | + entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); | ||
197 | + } | ||
198 | + } | ||
199 | + TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); | ||
200 | + if (tenantId.isNullUid()) { | ||
201 | + if (entity instanceof HasTenantId) { | ||
202 | + tenantId = ((HasTenantId) entity).getTenantId(); | ||
203 | + } | ||
204 | + } | ||
205 | + tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); | ||
206 | + } catch (Exception e) { | ||
207 | + log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); | ||
208 | + } | ||
209 | + } | ||
210 | + } | ||
211 | + | ||
212 | + | ||
213 | + private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { | ||
214 | + T result = null; | ||
215 | + if (additionalInfo != null && additionalInfo.length > index) { | ||
216 | + Object paramObject = additionalInfo[index]; | ||
217 | + if (clazz.isInstance(paramObject)) { | ||
218 | + result = clazz.cast(paramObject); | ||
219 | + } | ||
220 | + } | ||
221 | + return result; | ||
222 | + } | ||
223 | + | ||
224 | + private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { | ||
225 | + if (timeseries != null && !timeseries.isEmpty()) { | ||
226 | + ArrayNode result = entityNode.putArray("timeseries"); | ||
227 | + Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream() | ||
228 | + .collect(Collectors.groupingBy(TsKvEntry::getTs)); | ||
229 | + for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) { | ||
230 | + ObjectNode element = json.createObjectNode(); | ||
231 | + element.put("ts", entry.getKey()); | ||
232 | + ObjectNode values = element.putObject("values"); | ||
233 | + for (TsKvEntry tsKvEntry : entry.getValue()) { | ||
234 | + addKvEntry(values, tsKvEntry); | ||
235 | + } | ||
236 | + result.add(element); | ||
237 | + } | ||
238 | + } | ||
239 | + } | ||
240 | + | ||
241 | + private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { | ||
242 | + if (kvEntry.getDataType() == DataType.BOOLEAN) { | ||
243 | + kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
244 | + } else if (kvEntry.getDataType() == DataType.DOUBLE) { | ||
245 | + kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
246 | + } else if (kvEntry.getDataType() == DataType.LONG) { | ||
247 | + kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); | ||
248 | + } else if (kvEntry.getDataType() == DataType.JSON) { | ||
249 | + if (kvEntry.getJsonValue().isPresent()) { | ||
250 | + entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); | ||
251 | + } | ||
252 | + } else { | ||
253 | + entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); | ||
254 | + } | ||
255 | + } | ||
256 | +} |
@@ -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,37 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { | @@ -280,11 +283,37 @@ 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 (otaPackage.hasUrl()) { | ||
289 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); | ||
290 | + List<String> attrToRemove = new ArrayList<>(); | ||
291 | + | ||
292 | + if (otaPackage.getDataSize() == null) { | ||
293 | + attrToRemove.add(getAttributeKey(otaPackageType, SIZE)); | ||
294 | + } else { | ||
295 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); | ||
296 | + } | ||
297 | + | ||
298 | + if (otaPackage.getChecksumAlgorithm() != null) { | ||
299 | + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM)); | ||
300 | + } else { | ||
301 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); | ||
302 | + } | ||
303 | + | ||
304 | + if (StringUtils.isEmpty(otaPackage.getChecksum())) { | ||
305 | + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM)); | ||
306 | + } else { | ||
307 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); | ||
308 | + } | ||
309 | + | ||
310 | + remove(device, otaPackageType, attrToRemove); | ||
311 | + } else { | ||
312 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); | ||
313 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); | ||
314 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); | ||
315 | + remove(device, otaPackageType, Collections.singletonList(getAttributeKey(otaPackageType, URL))); | ||
316 | + } | ||
288 | 317 | ||
289 | telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { | 318 | telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { |
290 | @Override | 319 | @Override |
@@ -299,20 +328,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { | @@ -299,20 +328,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { | ||
299 | }); | 328 | }); |
300 | } | 329 | } |
301 | 330 | ||
302 | - private void remove(Device device, OtaPackageType firmwareType) { | ||
303 | - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType), | 331 | + private void remove(Device device, OtaPackageType otaPackageType) { |
332 | + remove(device, otaPackageType, OtaPackageUtil.getAttributeKeys(otaPackageType)); | ||
333 | + } | ||
334 | + | ||
335 | + private void remove(Device device, OtaPackageType otaPackageType, List<String> attributesKeys) { | ||
336 | + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, attributesKeys, | ||
304 | new FutureCallback<>() { | 337 | new FutureCallback<>() { |
305 | @Override | 338 | @Override |
306 | public void onSuccess(@Nullable Void tmp) { | 339 | public void onSuccess(@Nullable Void tmp) { |
307 | - log.trace("[{}] Success remove target firmware attributes!", device.getId()); | 340 | + log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); |
308 | Set<AttributeKey> keysToNotify = new HashSet<>(); | 341 | Set<AttributeKey> keysToNotify = new HashSet<>(); |
309 | - OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); | 342 | + attributesKeys.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); |
310 | tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); | 343 | tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); |
311 | } | 344 | } |
312 | 345 | ||
313 | @Override | 346 | @Override |
314 | public void onFailure(Throwable t) { | 347 | public void onFailure(Throwable t) { |
315 | - log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); | 348 | + log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); |
316 | } | 349 | } |
317 | }); | 350 | }); |
318 | } | 351 | } |
@@ -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)) { |
@@ -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()); |
@@ -17,9 +17,9 @@ package org.thingsboard.server.service.ttl; | @@ -17,9 +17,9 @@ package org.thingsboard.server.service.ttl; | ||
17 | 17 | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.beans.factory.annotation.Value; | 19 | import org.springframework.beans.factory.annotation.Value; |
20 | -import org.thingsboard.server.dao.util.PsqlDao; | ||
21 | 20 | ||
22 | import java.sql.Connection; | 21 | import java.sql.Connection; |
22 | +import java.sql.DriverManager; | ||
23 | import java.sql.ResultSet; | 23 | import java.sql.ResultSet; |
24 | import java.sql.SQLException; | 24 | import java.sql.SQLException; |
25 | import java.sql.SQLWarning; | 25 | import java.sql.SQLWarning; |
@@ -62,4 +62,8 @@ public abstract class AbstractCleanUpService { | @@ -62,4 +62,8 @@ public abstract class AbstractCleanUpService { | ||
62 | 62 | ||
63 | protected abstract void doCleanUp(Connection connection) throws SQLException; | 63 | protected abstract void doCleanUp(Connection connection) throws SQLException; |
64 | 64 | ||
65 | + protected Connection getConnection() throws SQLException { | ||
66 | + return DriverManager.getConnection(dbUrl, dbUserName, dbPassword); | ||
67 | + } | ||
68 | + | ||
65 | } | 69 | } |
application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java
0 → 100644
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.service.ttl.alarms; | ||
17 | + | ||
18 | +import lombok.RequiredArgsConstructor; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.springframework.beans.factory.annotation.Value; | ||
21 | +import org.springframework.scheduling.annotation.Scheduled; | ||
22 | +import org.springframework.stereotype.Service; | ||
23 | +import org.thingsboard.server.common.data.alarm.Alarm; | ||
24 | +import org.thingsboard.server.common.data.audit.ActionType; | ||
25 | +import org.thingsboard.server.common.data.id.AlarmId; | ||
26 | +import org.thingsboard.server.common.data.id.TenantId; | ||
27 | +import org.thingsboard.server.common.data.page.PageData; | ||
28 | +import org.thingsboard.server.common.data.page.PageLink; | ||
29 | +import org.thingsboard.server.common.data.page.SortOrder; | ||
30 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
31 | +import org.thingsboard.server.common.msg.queue.ServiceType; | ||
32 | +import org.thingsboard.server.dao.alarm.AlarmDao; | ||
33 | +import org.thingsboard.server.dao.alarm.AlarmService; | ||
34 | +import org.thingsboard.server.dao.relation.RelationService; | ||
35 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | ||
36 | +import org.thingsboard.server.dao.tenant.TenantDao; | ||
37 | +import org.thingsboard.server.dao.util.PsqlDao; | ||
38 | +import org.thingsboard.server.queue.discovery.PartitionService; | ||
39 | +import org.thingsboard.server.queue.util.TbCoreComponent; | ||
40 | +import org.thingsboard.server.service.action.RuleEngineEntityActionService; | ||
41 | +import org.thingsboard.server.service.ttl.AbstractCleanUpService; | ||
42 | + | ||
43 | +import java.sql.Connection; | ||
44 | +import java.sql.SQLException; | ||
45 | +import java.util.Date; | ||
46 | +import java.util.Optional; | ||
47 | +import java.util.UUID; | ||
48 | +import java.util.concurrent.TimeUnit; | ||
49 | + | ||
50 | +@TbCoreComponent | ||
51 | +@Service | ||
52 | +@Slf4j | ||
53 | +@RequiredArgsConstructor | ||
54 | +public class AlarmsCleanUpService { | ||
55 | + @Value("${sql.ttl.alarms.removal_batch_size}") | ||
56 | + private Integer removalBatchSize; | ||
57 | + | ||
58 | + private final TenantDao tenantDao; | ||
59 | + private final AlarmDao alarmDao; | ||
60 | + private final AlarmService alarmService; | ||
61 | + private final RelationService relationService; | ||
62 | + private final RuleEngineEntityActionService ruleEngineEntityActionService; | ||
63 | + private final PartitionService partitionService; | ||
64 | + private final TbTenantProfileCache tenantProfileCache; | ||
65 | + | ||
66 | + @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.alarms.checking_interval})}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") | ||
67 | + public void cleanUp() { | ||
68 | + PageLink tenantsBatchRequest = new PageLink(10_000, 0); | ||
69 | + PageLink removalBatchRequest = new PageLink(removalBatchSize, 0 ); | ||
70 | + PageData<TenantId> tenantsIds; | ||
71 | + do { | ||
72 | + tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); | ||
73 | + for (TenantId tenantId : tenantsIds.getData()) { | ||
74 | + if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { | ||
75 | + continue; | ||
76 | + } | ||
77 | + | ||
78 | + Optional<DefaultTenantProfileConfiguration> tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); | ||
79 | + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { | ||
80 | + continue; | ||
81 | + } | ||
82 | + | ||
83 | + long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); | ||
84 | + long expirationTime = System.currentTimeMillis() - ttl; | ||
85 | + | ||
86 | + long totalRemoved = 0; | ||
87 | + while (true) { | ||
88 | + PageData<AlarmId> toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest); | ||
89 | + toRemove.getData().forEach(alarmId -> { | ||
90 | + relationService.deleteEntityRelations(tenantId, alarmId); | ||
91 | + Alarm alarm = alarmService.deleteAlarm(tenantId, alarmId).getAlarm(); | ||
92 | + ruleEngineEntityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); | ||
93 | + }); | ||
94 | + | ||
95 | + totalRemoved += toRemove.getTotalElements(); | ||
96 | + if (!toRemove.hasNext()) { | ||
97 | + break; | ||
98 | + } | ||
99 | + } | ||
100 | + | ||
101 | + if (totalRemoved > 0) { | ||
102 | + log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); | ||
107 | + } while (tenantsIds.hasNext()); | ||
108 | + } | ||
109 | + | ||
110 | +} |
@@ -40,7 +40,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { | @@ -40,7 +40,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { | ||
40 | @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") | 40 | @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") |
41 | public void cleanUp() { | 41 | public void cleanUp() { |
42 | if (ttlTaskExecutionEnabled) { | 42 | if (ttlTaskExecutionEnabled) { |
43 | - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 43 | + try (Connection conn = getConnection()) { |
44 | doCleanUp(conn); | 44 | doCleanUp(conn); |
45 | } catch (SQLException e) { | 45 | } catch (SQLException e) { |
46 | log.error("SQLException occurred during TTL task execution ", e); | 46 | log.error("SQLException occurred during TTL task execution ", e); |
@@ -43,7 +43,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { | @@ -43,7 +43,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { | ||
43 | @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") | 43 | @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") |
44 | public void cleanUp() { | 44 | public void cleanUp() { |
45 | if (ttlTaskExecutionEnabled) { | 45 | if (ttlTaskExecutionEnabled) { |
46 | - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 46 | + try (Connection conn = getConnection()) { |
47 | doCleanUp(conn); | 47 | doCleanUp(conn); |
48 | } catch (SQLException e) { | 48 | } catch (SQLException e) { |
49 | log.error("SQLException occurred during TTL task execution ", e); | 49 | log.error("SQLException occurred during TTL task execution ", e); |
@@ -36,7 +36,7 @@ public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpSe | @@ -36,7 +36,7 @@ public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpSe | ||
36 | @Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}") | 36 | @Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}") |
37 | public void cleanUp() { | 37 | public void cleanUp() { |
38 | if (ttlTaskExecutionEnabled) { | 38 | if (ttlTaskExecutionEnabled) { |
39 | - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 39 | + try (Connection conn = getConnection()) { |
40 | doCleanUp(conn); | 40 | doCleanUp(conn); |
41 | } catch (SQLException e) { | 41 | } catch (SQLException e) { |
42 | log.error("SQLException occurred during TTL task execution ", e); | 42 | log.error("SQLException occurred during TTL task execution ", e); |
@@ -273,6 +273,9 @@ sql: | @@ -273,6 +273,9 @@ sql: | ||
273 | enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" | 273 | enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" |
274 | execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day | 274 | execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day |
275 | edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month | 275 | edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month |
276 | + alarms: | ||
277 | + checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours | ||
278 | + removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches | ||
276 | 279 | ||
277 | # Actor system parameters | 280 | # Actor system parameters |
278 | actors: | 281 | actors: |
@@ -50,7 +50,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | @@ -50,7 +50,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | ||
50 | private static final String FILE_NAME = "filename.txt"; | 50 | private static final String FILE_NAME = "filename.txt"; |
51 | private static final String VERSION = "v1.0"; | 51 | private static final String VERSION = "v1.0"; |
52 | private static final String CONTENT_TYPE = "text/plain"; | 52 | private static final String CONTENT_TYPE = "text/plain"; |
53 | - private static final String CHECKSUM_ALGORITHM = "sha256"; | 53 | + private static final String CHECKSUM_ALGORITHM = "SHA256"; |
54 | private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; | 54 | private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; |
55 | private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); | 55 | private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); |
56 | 56 | ||
@@ -257,7 +257,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | @@ -257,7 +257,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | ||
257 | @Test | 257 | @Test |
258 | public void testFindTenantFirmwaresByHasData() throws Exception { | 258 | public void testFindTenantFirmwaresByHasData() throws Exception { |
259 | List<OtaPackageInfo> otaPackagesWithData = new ArrayList<>(); | 259 | List<OtaPackageInfo> otaPackagesWithData = new ArrayList<>(); |
260 | - List<OtaPackageInfo> otaPackagesWithoutData = new ArrayList<>(); | 260 | + List<OtaPackageInfo> allOtaPackages = new ArrayList<>(); |
261 | 261 | ||
262 | for (int i = 0; i < 165; i++) { | 262 | for (int i = 0; i < 165; i++) { |
263 | OtaPackageInfo firmwareInfo = new OtaPackageInfo(); | 263 | OtaPackageInfo firmwareInfo = new OtaPackageInfo(); |
@@ -272,44 +272,45 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | @@ -272,44 +272,45 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes | ||
272 | MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); | 272 | MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); |
273 | 273 | ||
274 | OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); | 274 | OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); |
275 | - otaPackagesWithData.add(new OtaPackageInfo(savedFirmware)); | ||
276 | - } else { | ||
277 | - otaPackagesWithoutData.add(savedFirmwareInfo); | 275 | + savedFirmwareInfo = new OtaPackageInfo(savedFirmware); |
276 | + otaPackagesWithData.add(savedFirmwareInfo); | ||
278 | } | 277 | } |
278 | + | ||
279 | + allOtaPackages.add(savedFirmwareInfo); | ||
279 | } | 280 | } |
280 | 281 | ||
281 | - List<OtaPackageInfo> loadedFirmwaresWithData = new ArrayList<>(); | 282 | + List<OtaPackageInfo> loadedOtaPackagesWithData = new ArrayList<>(); |
282 | PageLink pageLink = new PageLink(24); | 283 | PageLink pageLink = new PageLink(24); |
283 | PageData<OtaPackageInfo> pageData; | 284 | PageData<OtaPackageInfo> pageData; |
284 | do { | 285 | do { |
285 | - pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/true?", | 286 | + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE?", |
286 | new TypeReference<>() { | 287 | new TypeReference<>() { |
287 | }, pageLink); | 288 | }, pageLink); |
288 | - loadedFirmwaresWithData.addAll(pageData.getData()); | 289 | + loadedOtaPackagesWithData.addAll(pageData.getData()); |
289 | if (pageData.hasNext()) { | 290 | if (pageData.hasNext()) { |
290 | pageLink = pageLink.nextPageLink(); | 291 | pageLink = pageLink.nextPageLink(); |
291 | } | 292 | } |
292 | } while (pageData.hasNext()); | 293 | } while (pageData.hasNext()); |
293 | 294 | ||
294 | - List<OtaPackageInfo> loadedFirmwaresWithoutData = new ArrayList<>(); | 295 | + List<OtaPackageInfo> allLoadedOtaPackages = new ArrayList<>(); |
295 | pageLink = new PageLink(24); | 296 | pageLink = new PageLink(24); |
296 | do { | 297 | do { |
297 | - pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/false?", | 298 | + pageData = doGetTypedWithPageLink("/api/otaPackages?", |
298 | new TypeReference<>() { | 299 | new TypeReference<>() { |
299 | }, pageLink); | 300 | }, pageLink); |
300 | - loadedFirmwaresWithoutData.addAll(pageData.getData()); | 301 | + allLoadedOtaPackages.addAll(pageData.getData()); |
301 | if (pageData.hasNext()) { | 302 | if (pageData.hasNext()) { |
302 | pageLink = pageLink.nextPageLink(); | 303 | pageLink = pageLink.nextPageLink(); |
303 | } | 304 | } |
304 | } while (pageData.hasNext()); | 305 | } while (pageData.hasNext()); |
305 | 306 | ||
306 | Collections.sort(otaPackagesWithData, idComparator); | 307 | Collections.sort(otaPackagesWithData, idComparator); |
307 | - Collections.sort(otaPackagesWithoutData, idComparator); | ||
308 | - Collections.sort(loadedFirmwaresWithData, idComparator); | ||
309 | - Collections.sort(loadedFirmwaresWithoutData, idComparator); | 308 | + Collections.sort(allOtaPackages, idComparator); |
309 | + Collections.sort(loadedOtaPackagesWithData, idComparator); | ||
310 | + Collections.sort(allLoadedOtaPackages, idComparator); | ||
310 | 311 | ||
311 | - Assert.assertEquals(otaPackagesWithData, loadedFirmwaresWithData); | ||
312 | - Assert.assertEquals(otaPackagesWithoutData, loadedFirmwaresWithoutData); | 312 | + Assert.assertEquals(otaPackagesWithData, loadedOtaPackagesWithData); |
313 | + Assert.assertEquals(allOtaPackages, allLoadedOtaPackages); | ||
313 | } | 314 | } |
314 | 315 | ||
315 | 316 |
@@ -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 | } |
@@ -66,6 +66,7 @@ public class DataConstants { | @@ -66,6 +66,7 @@ public class DataConstants { | ||
66 | public static final String TIMESERIES_DELETED = "TIMESERIES_DELETED"; | 66 | public static final String TIMESERIES_DELETED = "TIMESERIES_DELETED"; |
67 | public static final String ALARM_ACK = "ALARM_ACK"; | 67 | public static final String ALARM_ACK = "ALARM_ACK"; |
68 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; | 68 | public static final String ALARM_CLEAR = "ALARM_CLEAR"; |
69 | + public static final String ALARM_DELETE = "ALARM_DELETE"; | ||
69 | public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT"; | 70 | public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT"; |
70 | public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT"; | 71 | public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT"; |
71 | public static final String PROVISION_SUCCESS = "PROVISION_SUCCESS"; | 72 | public static final String PROVISION_SUCCESS = "PROVISION_SUCCESS"; |
@@ -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 | } |
@@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; | @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; | ||
27 | 27 | ||
28 | import java.io.ByteArrayInputStream; | 28 | import java.io.ByteArrayInputStream; |
29 | import java.io.IOException; | 29 | import java.io.IOException; |
30 | +import java.util.Optional; | ||
30 | 31 | ||
31 | import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; | 32 | import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; |
32 | 33 | ||
@@ -92,6 +93,13 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H | @@ -92,6 +93,13 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H | ||
92 | } | 93 | } |
93 | } | 94 | } |
94 | 95 | ||
96 | + @JsonIgnore | ||
97 | + public Optional<DefaultTenantProfileConfiguration> getProfileConfiguration() { | ||
98 | + return Optional.ofNullable(getProfileData().getConfiguration()) | ||
99 | + .filter(profileConfiguration -> profileConfiguration instanceof DefaultTenantProfileConfiguration) | ||
100 | + .map(profileConfiguration -> (DefaultTenantProfileConfiguration) profileConfiguration); | ||
101 | + } | ||
102 | + | ||
95 | public TenantProfileData createDefaultTenantProfileData() { | 103 | public TenantProfileData createDefaultTenantProfileData() { |
96 | TenantProfileData tpd = new TenantProfileData(); | 104 | TenantProfileData tpd = new TenantProfileData(); |
97 | tpd.setConfiguration(new DefaultTenantProfileConfiguration()); | 105 | tpd.setConfiguration(new DefaultTenantProfileConfiguration()); |
@@ -39,6 +39,7 @@ public enum ActionType { | @@ -39,6 +39,7 @@ public enum ActionType { | ||
39 | RELATIONS_DELETED(false), | 39 | RELATIONS_DELETED(false), |
40 | ALARM_ACK(false), | 40 | ALARM_ACK(false), |
41 | ALARM_CLEAR(false), | 41 | ALARM_CLEAR(false), |
42 | + ALARM_DELETE(false), | ||
42 | LOGIN(false), | 43 | LOGIN(false), |
43 | LOGOUT(false), | 44 | LOGOUT(false), |
44 | LOCKOUT(false), | 45 | LOCKOUT(false), |
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.common.data.exception; | ||
17 | + | ||
18 | +public class ApiUsageLimitsExceededException extends RuntimeException { | ||
19 | + public ApiUsageLimitsExceededException(String message) { | ||
20 | + super(message); | ||
21 | + } | ||
22 | + | ||
23 | + public ApiUsageLimitsExceededException() { | ||
24 | + } | ||
25 | +} |
@@ -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; |
@@ -17,10 +17,11 @@ package org.thingsboard.server.common.data.page; | @@ -17,10 +17,11 @@ package org.thingsboard.server.common.data.page; | ||
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonCreator; | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | import com.fasterxml.jackson.annotation.JsonProperty; | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
20 | -import org.thingsboard.server.common.data.BaseData; | ||
21 | 20 | ||
22 | import java.util.Collections; | 21 | import java.util.Collections; |
23 | import java.util.List; | 22 | import java.util.List; |
23 | +import java.util.function.Function; | ||
24 | +import java.util.stream.Collectors; | ||
24 | 25 | ||
25 | public class PageData<T> { | 26 | public class PageData<T> { |
26 | 27 | ||
@@ -61,4 +62,8 @@ public class PageData<T> { | @@ -61,4 +62,8 @@ public class PageData<T> { | ||
61 | return hasNext; | 62 | return hasNext; |
62 | } | 63 | } |
63 | 64 | ||
65 | + public <D> PageData<D> mapData(Function<T, D> mapper) { | ||
66 | + return new PageData<>(getData().stream().map(mapper).collect(Collectors.toList()), getTotalPages(), getTotalElements(), hasNext()); | ||
67 | + } | ||
68 | + | ||
64 | } | 69 | } |
@@ -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; |
@@ -53,6 +55,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura | @@ -53,6 +55,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura | ||
53 | private long maxCreatedAlarms; | 55 | private long maxCreatedAlarms; |
54 | 56 | ||
55 | private int defaultStorageTtlDays; | 57 | private int defaultStorageTtlDays; |
58 | + private int alarmsTtlDays; | ||
56 | 59 | ||
57 | private double warnThreshold; | 60 | private double warnThreshold; |
58 | 61 |
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao; | @@ -18,6 +18,7 @@ package org.thingsboard.server.dao; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.id.TenantId; | 19 | import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | ||
21 | +import java.util.Collection; | ||
21 | import java.util.List; | 22 | import java.util.List; |
22 | import java.util.UUID; | 23 | import java.util.UUID; |
23 | 24 | ||
@@ -33,4 +34,6 @@ public interface Dao<T> { | @@ -33,4 +34,6 @@ public interface Dao<T> { | ||
33 | 34 | ||
34 | boolean removeById(TenantId tenantId, UUID id); | 35 | boolean removeById(TenantId tenantId, UUID id); |
35 | 36 | ||
37 | + void removeAllByIds(Collection<UUID> ids); | ||
38 | + | ||
36 | } | 39 | } |
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 | +} |
@@ -21,10 +21,12 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; | @@ -21,10 +21,12 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
21 | import org.thingsboard.server.common.data.alarm.AlarmQuery; | 21 | import org.thingsboard.server.common.data.alarm.AlarmQuery; |
22 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 22 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
23 | import org.thingsboard.server.common.data.alarm.AlarmStatus; | 23 | import org.thingsboard.server.common.data.alarm.AlarmStatus; |
24 | +import org.thingsboard.server.common.data.id.AlarmId; | ||
24 | import org.thingsboard.server.common.data.id.CustomerId; | 25 | import org.thingsboard.server.common.data.id.CustomerId; |
25 | import org.thingsboard.server.common.data.id.EntityId; | 26 | import org.thingsboard.server.common.data.id.EntityId; |
26 | import org.thingsboard.server.common.data.id.TenantId; | 27 | import org.thingsboard.server.common.data.id.TenantId; |
27 | import org.thingsboard.server.common.data.page.PageData; | 28 | import org.thingsboard.server.common.data.page.PageData; |
29 | +import org.thingsboard.server.common.data.page.PageLink; | ||
28 | import org.thingsboard.server.common.data.query.AlarmData; | 30 | import org.thingsboard.server.common.data.query.AlarmData; |
29 | import org.thingsboard.server.common.data.query.AlarmDataQuery; | 31 | import org.thingsboard.server.common.data.query.AlarmDataQuery; |
30 | import org.thingsboard.server.dao.Dao; | 32 | import org.thingsboard.server.dao.Dao; |
@@ -54,4 +56,7 @@ public interface AlarmDao extends Dao<Alarm> { | @@ -54,4 +56,7 @@ public interface AlarmDao extends Dao<Alarm> { | ||
54 | AlarmDataQuery query, Collection<EntityId> orderedEntityIds); | 56 | AlarmDataQuery query, Collection<EntityId> orderedEntityIds); |
55 | 57 | ||
56 | Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> status); | 58 | Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> status); |
59 | + | ||
60 | + PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); | ||
61 | + | ||
57 | } | 62 | } |
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; | @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; | ||
34 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; | 34 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; |
35 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 35 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
36 | import org.thingsboard.server.common.data.alarm.AlarmStatus; | 36 | import org.thingsboard.server.common.data.alarm.AlarmStatus; |
37 | +import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; | ||
37 | import org.thingsboard.server.common.data.id.AlarmId; | 38 | import org.thingsboard.server.common.data.id.AlarmId; |
38 | import org.thingsboard.server.common.data.id.CustomerId; | 39 | import org.thingsboard.server.common.data.id.CustomerId; |
39 | import org.thingsboard.server.common.data.id.EntityId; | 40 | import org.thingsboard.server.common.data.id.EntityId; |
@@ -119,7 +120,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | @@ -119,7 +120,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ | ||
119 | Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get(); | 120 | Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get(); |
120 | if (existing == null || existing.getStatus().isCleared()) { | 121 | if (existing == null || existing.getStatus().isCleared()) { |
121 | if (!alarmCreationEnabled) { | 122 | if (!alarmCreationEnabled) { |
122 | - throw new IllegalStateException("Alarm creation is disabled"); | 123 | + throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); |
123 | } | 124 | } |
124 | return createAlarm(alarm); | 125 | return createAlarm(alarm); |
125 | } else { | 126 | } else { |
@@ -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 (!otaPackage.hasUrl()) { |
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(); |
@@ -26,6 +26,7 @@ import org.thingsboard.server.dao.Dao; | @@ -26,6 +26,7 @@ import org.thingsboard.server.dao.Dao; | ||
26 | import org.thingsboard.server.dao.DaoUtil; | 26 | import org.thingsboard.server.dao.DaoUtil; |
27 | import org.thingsboard.server.dao.model.BaseEntity; | 27 | import org.thingsboard.server.dao.model.BaseEntity; |
28 | 28 | ||
29 | +import java.util.Collection; | ||
29 | import java.util.List; | 30 | import java.util.List; |
30 | import java.util.Optional; | 31 | import java.util.Optional; |
31 | import java.util.UUID; | 32 | import java.util.UUID; |
@@ -87,6 +88,12 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D> | @@ -87,6 +88,12 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D> | ||
87 | return !getCrudRepository().existsById(id); | 88 | return !getCrudRepository().existsById(id); |
88 | } | 89 | } |
89 | 90 | ||
91 | + @Transactional | ||
92 | + public void removeAllByIds(Collection<UUID> ids) { | ||
93 | + CrudRepository<E, UUID> repository = getCrudRepository(); | ||
94 | + ids.forEach(repository::deleteById); | ||
95 | + } | ||
96 | + | ||
90 | @Override | 97 | @Override |
91 | public List<D> find(TenantId tenantId) { | 98 | public List<D> find(TenantId tenantId) { |
92 | List<E> entities = Lists.newArrayList(getCrudRepository().findAll()); | 99 | List<E> entities = Lists.newArrayList(getCrudRepository().findAll()); |
@@ -17,11 +17,13 @@ package org.thingsboard.server.dao.sql.alarm; | @@ -17,11 +17,13 @@ package org.thingsboard.server.dao.sql.alarm; | ||
17 | 17 | ||
18 | import org.springframework.data.domain.Page; | 18 | import org.springframework.data.domain.Page; |
19 | import org.springframework.data.domain.Pageable; | 19 | import org.springframework.data.domain.Pageable; |
20 | +import org.springframework.data.jpa.repository.Modifying; | ||
20 | import org.springframework.data.jpa.repository.Query; | 21 | import org.springframework.data.jpa.repository.Query; |
21 | import org.springframework.data.repository.CrudRepository; | 22 | import org.springframework.data.repository.CrudRepository; |
22 | import org.springframework.data.repository.query.Param; | 23 | import org.springframework.data.repository.query.Param; |
23 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 24 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
24 | import org.thingsboard.server.common.data.alarm.AlarmStatus; | 25 | import org.thingsboard.server.common.data.alarm.AlarmStatus; |
26 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | import org.thingsboard.server.dao.model.sql.AlarmEntity; | 27 | import org.thingsboard.server.dao.model.sql.AlarmEntity; |
26 | import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; | 28 | import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; |
27 | 29 | ||
@@ -159,4 +161,8 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> { | @@ -159,4 +161,8 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> { | ||
159 | @Param("affectedEntityId") UUID affectedEntityId, | 161 | @Param("affectedEntityId") UUID affectedEntityId, |
160 | @Param("affectedEntityType") String affectedEntityType, | 162 | @Param("affectedEntityType") String affectedEntityType, |
161 | @Param("alarmStatuses") Set<AlarmStatus> alarmStatuses); | 163 | @Param("alarmStatuses") Set<AlarmStatus> alarmStatuses); |
164 | + | ||
165 | + @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time") | ||
166 | + Page<UUID> findAlarmsIdsByEndTsBeforeAndTenantId(@Param("time") Long time, @Param("tenantId") UUID tenantId, Pageable pageable); | ||
167 | + | ||
162 | } | 168 | } |
@@ -26,10 +26,12 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; | @@ -26,10 +26,12 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
26 | import org.thingsboard.server.common.data.alarm.AlarmQuery; | 26 | import org.thingsboard.server.common.data.alarm.AlarmQuery; |
27 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 27 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
28 | import org.thingsboard.server.common.data.alarm.AlarmStatus; | 28 | import org.thingsboard.server.common.data.alarm.AlarmStatus; |
29 | +import org.thingsboard.server.common.data.id.AlarmId; | ||
29 | import org.thingsboard.server.common.data.id.CustomerId; | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
30 | import org.thingsboard.server.common.data.id.EntityId; | 31 | import org.thingsboard.server.common.data.id.EntityId; |
31 | import org.thingsboard.server.common.data.id.TenantId; | 32 | import org.thingsboard.server.common.data.id.TenantId; |
32 | import org.thingsboard.server.common.data.page.PageData; | 33 | import org.thingsboard.server.common.data.page.PageData; |
34 | +import org.thingsboard.server.common.data.page.PageLink; | ||
33 | import org.thingsboard.server.common.data.query.AlarmData; | 35 | import org.thingsboard.server.common.data.query.AlarmData; |
34 | import org.thingsboard.server.common.data.query.AlarmDataQuery; | 36 | import org.thingsboard.server.common.data.query.AlarmDataQuery; |
35 | import org.thingsboard.server.dao.DaoUtil; | 37 | import org.thingsboard.server.dao.DaoUtil; |
@@ -161,4 +163,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A | @@ -161,4 +163,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A | ||
161 | public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> statuses) { | 163 | public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> statuses) { |
162 | return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(), statuses); | 164 | return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(), statuses); |
163 | } | 165 | } |
166 | + | ||
167 | + @Override | ||
168 | + public PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink) { | ||
169 | + return DaoUtil.pageToPageData(alarmRepository.findAlarmsIdsByEndTsBeforeAndTenantId(time, tenantId.getId(), DaoUtil.toPageable(pageLink))) | ||
170 | + .mapData(AlarmId::new); | ||
171 | + } | ||
164 | } | 172 | } |
@@ -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 | } |
@@ -74,4 +74,10 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant> | @@ -74,4 +74,10 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant> | ||
74 | Objects.toString(pageLink.getTextSearch(), ""), | 74 | Objects.toString(pageLink.getTextSearch(), ""), |
75 | DaoUtil.toPageable(pageLink, TenantInfoEntity.tenantInfoColumnMap))); | 75 | DaoUtil.toPageable(pageLink, TenantInfoEntity.tenantInfoColumnMap))); |
76 | } | 76 | } |
77 | + | ||
78 | + @Override | ||
79 | + public PageData<TenantId> findTenantsIds(PageLink pageLink) { | ||
80 | + return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::new); | ||
81 | + } | ||
82 | + | ||
77 | } | 83 | } |
@@ -50,4 +50,8 @@ public interface TenantRepository extends PagingAndSortingRepository<TenantEntit | @@ -50,4 +50,8 @@ public interface TenantRepository extends PagingAndSortingRepository<TenantEntit | ||
50 | Page<TenantInfoEntity> findTenantInfoByRegionNextPage(@Param("region") String region, | 50 | Page<TenantInfoEntity> findTenantInfoByRegionNextPage(@Param("region") String region, |
51 | @Param("textSearch") String textSearch, | 51 | @Param("textSearch") String textSearch, |
52 | Pageable pageable); | 52 | Pageable pageable); |
53 | + | ||
54 | + @Query("SELECT t.id FROM TenantEntity t") | ||
55 | + Page<UUID> findTenantsIds(Pageable pageable); | ||
56 | + | ||
53 | } | 57 | } |
@@ -46,5 +46,7 @@ public interface TenantDao extends Dao<Tenant> { | @@ -46,5 +46,7 @@ public interface TenantDao extends Dao<Tenant> { | ||
46 | PageData<Tenant> findTenantsByRegion(TenantId tenantId, String region, PageLink pageLink); | 46 | PageData<Tenant> findTenantsByRegion(TenantId tenantId, String region, PageLink pageLink); |
47 | 47 | ||
48 | PageData<TenantInfo> findTenantInfosByRegion(TenantId tenantId, String region, PageLink pageLink); | 48 | PageData<TenantInfo> findTenantInfosByRegion(TenantId tenantId, String region, PageLink pageLink); |
49 | - | 49 | + |
50 | + PageData<TenantId> findTenantsIds(PageLink pageLink); | ||
51 | + | ||
50 | } | 52 | } |
@@ -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), |
@@ -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 | } |
@@ -24,7 +24,7 @@ const topicProperties = config.get('kafka.topic_properties'); | @@ -24,7 +24,7 @@ const topicProperties = config.get('kafka.topic_properties'); | ||
24 | const kafkaClientId = config.get('kafka.client_id'); | 24 | const kafkaClientId = config.get('kafka.client_id'); |
25 | const acks = Number(config.get('kafka.acks')); | 25 | const acks = Number(config.get('kafka.acks')); |
26 | const requestTimeout = Number(config.get('kafka.requestTimeout')); | 26 | const requestTimeout = Number(config.get('kafka.requestTimeout')); |
27 | -const compressionType = (config.get('kafka.requestTimeout') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; | 27 | +const compressionType = (config.get('kafka.compression') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; |
28 | 28 | ||
29 | let kafkaClient; | 29 | let kafkaClient; |
30 | let kafkaAdmin; | 30 | let kafkaAdmin; |
@@ -19,18 +19,25 @@ import com.fasterxml.jackson.databind.JsonNode; | @@ -19,18 +19,25 @@ import com.fasterxml.jackson.databind.JsonNode; | ||
19 | import com.fasterxml.jackson.databind.ObjectMapper; | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
20 | import com.fasterxml.jackson.databind.node.ObjectNode; | 20 | import com.fasterxml.jackson.databind.node.ObjectNode; |
21 | import org.springframework.core.ParameterizedTypeReference; | 21 | import org.springframework.core.ParameterizedTypeReference; |
22 | +import org.springframework.core.io.ByteArrayResource; | ||
23 | +import org.springframework.core.io.Resource; | ||
22 | import org.springframework.http.HttpEntity; | 24 | import org.springframework.http.HttpEntity; |
25 | +import org.springframework.http.HttpHeaders; | ||
23 | import org.springframework.http.HttpMethod; | 26 | import org.springframework.http.HttpMethod; |
24 | import org.springframework.http.HttpRequest; | 27 | import org.springframework.http.HttpRequest; |
25 | import org.springframework.http.HttpStatus; | 28 | import org.springframework.http.HttpStatus; |
29 | +import org.springframework.http.MediaType; | ||
26 | import org.springframework.http.ResponseEntity; | 30 | import org.springframework.http.ResponseEntity; |
27 | import org.springframework.http.client.ClientHttpRequestExecution; | 31 | import org.springframework.http.client.ClientHttpRequestExecution; |
28 | import org.springframework.http.client.ClientHttpRequestInterceptor; | 32 | import org.springframework.http.client.ClientHttpRequestInterceptor; |
29 | import org.springframework.http.client.ClientHttpResponse; | 33 | import org.springframework.http.client.ClientHttpResponse; |
30 | import org.springframework.http.client.support.HttpRequestWrapper; | 34 | import org.springframework.http.client.support.HttpRequestWrapper; |
35 | +import org.springframework.util.LinkedMultiValueMap; | ||
36 | +import org.springframework.util.MultiValueMap; | ||
31 | import org.springframework.util.StringUtils; | 37 | import org.springframework.util.StringUtils; |
32 | import org.springframework.web.client.HttpClientErrorException; | 38 | import org.springframework.web.client.HttpClientErrorException; |
33 | import org.springframework.web.client.RestTemplate; | 39 | import org.springframework.web.client.RestTemplate; |
40 | +import org.springframework.web.multipart.MultipartFile; | ||
34 | import org.thingsboard.common.util.ThingsBoardExecutors; | 41 | import org.thingsboard.common.util.ThingsBoardExecutors; |
35 | import org.thingsboard.rest.client.utils.RestJsonConverter; | 42 | import org.thingsboard.rest.client.utils.RestJsonConverter; |
36 | import org.thingsboard.server.common.data.AdminSettings; | 43 | import org.thingsboard.server.common.data.AdminSettings; |
@@ -48,6 +55,10 @@ import org.thingsboard.server.common.data.EntitySubtype; | @@ -48,6 +55,10 @@ import org.thingsboard.server.common.data.EntitySubtype; | ||
48 | import org.thingsboard.server.common.data.EntityView; | 55 | import org.thingsboard.server.common.data.EntityView; |
49 | import org.thingsboard.server.common.data.EntityViewInfo; | 56 | import org.thingsboard.server.common.data.EntityViewInfo; |
50 | import org.thingsboard.server.common.data.Event; | 57 | import org.thingsboard.server.common.data.Event; |
58 | +import org.thingsboard.server.common.data.OtaPackage; | ||
59 | +import org.thingsboard.server.common.data.OtaPackageInfo; | ||
60 | +import org.thingsboard.server.common.data.TbResource; | ||
61 | +import org.thingsboard.server.common.data.TbResourceInfo; | ||
51 | import org.thingsboard.server.common.data.Tenant; | 62 | import org.thingsboard.server.common.data.Tenant; |
52 | import org.thingsboard.server.common.data.TenantInfo; | 63 | import org.thingsboard.server.common.data.TenantInfo; |
53 | import org.thingsboard.server.common.data.TenantProfile; | 64 | import org.thingsboard.server.common.data.TenantProfile; |
@@ -78,8 +89,10 @@ import org.thingsboard.server.common.data.id.EdgeId; | @@ -78,8 +89,10 @@ import org.thingsboard.server.common.data.id.EdgeId; | ||
78 | import org.thingsboard.server.common.data.id.EntityId; | 89 | import org.thingsboard.server.common.data.id.EntityId; |
79 | import org.thingsboard.server.common.data.id.EntityViewId; | 90 | import org.thingsboard.server.common.data.id.EntityViewId; |
80 | import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | 91 | import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
92 | +import org.thingsboard.server.common.data.id.OtaPackageId; | ||
81 | import org.thingsboard.server.common.data.id.RuleChainId; | 93 | import org.thingsboard.server.common.data.id.RuleChainId; |
82 | import org.thingsboard.server.common.data.id.RuleNodeId; | 94 | import org.thingsboard.server.common.data.id.RuleNodeId; |
95 | +import org.thingsboard.server.common.data.id.TbResourceId; | ||
83 | import org.thingsboard.server.common.data.id.TenantId; | 96 | import org.thingsboard.server.common.data.id.TenantId; |
84 | import org.thingsboard.server.common.data.id.TenantProfileId; | 97 | import org.thingsboard.server.common.data.id.TenantProfileId; |
85 | import org.thingsboard.server.common.data.id.UserId; | 98 | import org.thingsboard.server.common.data.id.UserId; |
@@ -91,6 +104,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | @@ -91,6 +104,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
91 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; | 104 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
92 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | 105 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
93 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | 106 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; |
107 | +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; | ||
108 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | ||
94 | import org.thingsboard.server.common.data.page.PageData; | 109 | import org.thingsboard.server.common.data.page.PageData; |
95 | import org.thingsboard.server.common.data.page.PageLink; | 110 | import org.thingsboard.server.common.data.page.PageLink; |
96 | import org.thingsboard.server.common.data.page.SortOrder; | 111 | import org.thingsboard.server.common.data.page.SortOrder; |
@@ -127,9 +142,9 @@ import java.util.HashMap; | @@ -127,9 +142,9 @@ import java.util.HashMap; | ||
127 | import java.util.List; | 142 | import java.util.List; |
128 | import java.util.Map; | 143 | import java.util.Map; |
129 | import java.util.Optional; | 144 | import java.util.Optional; |
145 | +import java.util.UUID; | ||
130 | import java.util.concurrent.ConcurrentHashMap; | 146 | import java.util.concurrent.ConcurrentHashMap; |
131 | import java.util.concurrent.ExecutorService; | 147 | import java.util.concurrent.ExecutorService; |
132 | -import java.util.concurrent.Executors; | ||
133 | import java.util.concurrent.Future; | 148 | import java.util.concurrent.Future; |
134 | import java.util.stream.Collectors; | 149 | import java.util.stream.Collectors; |
135 | 150 | ||
@@ -147,7 +162,6 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -147,7 +162,6 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
147 | private final ObjectMapper objectMapper = new ObjectMapper(); | 162 | private final ObjectMapper objectMapper = new ObjectMapper(); |
148 | private ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass()); | 163 | private ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass()); |
149 | 164 | ||
150 | - | ||
151 | protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken="; | 165 | protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken="; |
152 | 166 | ||
153 | public RestClient(String baseURL) { | 167 | public RestClient(String baseURL) { |
@@ -1238,6 +1252,21 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -1238,6 +1252,21 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
1238 | HttpEntity.EMPTY, Device.class, tenantId, deviceId).getBody(); | 1252 | HttpEntity.EMPTY, Device.class, tenantId, deviceId).getBody(); |
1239 | } | 1253 | } |
1240 | 1254 | ||
1255 | + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { | ||
1256 | + Map<String, String> params = new HashMap<>(); | ||
1257 | + params.put("otaPackageType", otaPackageType.name()); | ||
1258 | + params.put("deviceProfileId", deviceProfileId.getId().toString()); | ||
1259 | + | ||
1260 | + return restTemplate.exchange( | ||
1261 | + baseURL + "/api/devices/count/{otaPackageType}?deviceProfileId={deviceProfileId}", | ||
1262 | + HttpMethod.GET, | ||
1263 | + HttpEntity.EMPTY, | ||
1264 | + new ParameterizedTypeReference<Long>() { | ||
1265 | + }, | ||
1266 | + params | ||
1267 | + ).getBody(); | ||
1268 | + } | ||
1269 | + | ||
1241 | @Deprecated | 1270 | @Deprecated |
1242 | public Device createDevice(String name, String type) { | 1271 | public Device createDevice(String name, String type) { |
1243 | Device device = new Device(); | 1272 | Device device = new Device(); |
@@ -2830,6 +2859,176 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -2830,6 +2859,176 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
2830 | restTemplate.postForEntity(baseURL + "/api/edge/sync/{edgeId}", null, EdgeId.class, params); | 2859 | restTemplate.postForEntity(baseURL + "/api/edge/sync/{edgeId}", null, EdgeId.class, params); |
2831 | } | 2860 | } |
2832 | 2861 | ||
2862 | + public ResponseEntity<Resource> downloadResource(TbResourceId resourceId) { | ||
2863 | + Map<String, String> params = new HashMap<>(); | ||
2864 | + params.put("resourceId", resourceId.getId().toString()); | ||
2865 | + | ||
2866 | + return restTemplate.exchange( | ||
2867 | + baseURL + "/api/resource/{resourceId}/download", | ||
2868 | + HttpMethod.GET, | ||
2869 | + HttpEntity.EMPTY, | ||
2870 | + new ParameterizedTypeReference<>() {}, | ||
2871 | + params | ||
2872 | + ); | ||
2873 | + } | ||
2874 | + | ||
2875 | + public TbResourceInfo getResourceInfoById(TbResourceId resourceId) { | ||
2876 | + Map<String, String> params = new HashMap<>(); | ||
2877 | + params.put("resourceId", resourceId.getId().toString()); | ||
2878 | + | ||
2879 | + return restTemplate.exchange( | ||
2880 | + baseURL + "/api/resource/info/{resourceId}", | ||
2881 | + HttpMethod.GET, | ||
2882 | + HttpEntity.EMPTY, | ||
2883 | + new ParameterizedTypeReference<TbResourceInfo>() {}, | ||
2884 | + params | ||
2885 | + ).getBody(); | ||
2886 | + } | ||
2887 | + | ||
2888 | + public TbResource getResourceId(TbResourceId resourceId) { | ||
2889 | + Map<String, String> params = new HashMap<>(); | ||
2890 | + params.put("resourceId", resourceId.getId().toString()); | ||
2891 | + | ||
2892 | + return restTemplate.exchange( | ||
2893 | + baseURL + "/api/resource/{resourceId}", | ||
2894 | + HttpMethod.GET, | ||
2895 | + HttpEntity.EMPTY, | ||
2896 | + new ParameterizedTypeReference<TbResource>() {}, | ||
2897 | + params | ||
2898 | + ).getBody(); | ||
2899 | + } | ||
2900 | + | ||
2901 | + public TbResource saveResource(TbResource resource) { | ||
2902 | + return restTemplate.postForEntity( | ||
2903 | + baseURL + "/api/resource", | ||
2904 | + resource, | ||
2905 | + TbResource.class | ||
2906 | + ).getBody(); | ||
2907 | + } | ||
2908 | + | ||
2909 | + public PageData<TbResourceInfo> getResources(PageLink pageLink) { | ||
2910 | + Map<String, String> params = new HashMap<>(); | ||
2911 | + addPageLinkToParam(params, pageLink); | ||
2912 | + return restTemplate.exchange( | ||
2913 | + baseURL + "/api/resource?" + getUrlParams(pageLink), | ||
2914 | + HttpMethod.GET, | ||
2915 | + HttpEntity.EMPTY, | ||
2916 | + new ParameterizedTypeReference<PageData<TbResourceInfo>>() {}, | ||
2917 | + params | ||
2918 | + ).getBody(); | ||
2919 | + } | ||
2920 | + | ||
2921 | + public void deleteResource(TbResourceId resourceId) { | ||
2922 | + restTemplate.delete("/api/resource/{resourceId}", resourceId.getId().toString()); | ||
2923 | + } | ||
2924 | + | ||
2925 | + public ResponseEntity<Resource> downloadOtaPackage(OtaPackageId otaPackageId) { | ||
2926 | + Map<String, String> params = new HashMap<>(); | ||
2927 | + params.put("otaPackageId", otaPackageId.getId().toString()); | ||
2928 | + | ||
2929 | + return restTemplate.exchange( | ||
2930 | + baseURL + "/api/otaPackage/{otaPackageId}/download", | ||
2931 | + HttpMethod.GET, | ||
2932 | + HttpEntity.EMPTY, | ||
2933 | + new ParameterizedTypeReference<>() {}, | ||
2934 | + params | ||
2935 | + ); | ||
2936 | + } | ||
2937 | + | ||
2938 | + public OtaPackageInfo getOtaPackageInfoById(OtaPackageId otaPackageId) { | ||
2939 | + Map<String, String> params = new HashMap<>(); | ||
2940 | + params.put("otaPackageId", otaPackageId.getId().toString()); | ||
2941 | + | ||
2942 | + return restTemplate.exchange( | ||
2943 | + baseURL + "/api/otaPackage/info/{otaPackageId}", | ||
2944 | + HttpMethod.GET, | ||
2945 | + HttpEntity.EMPTY, | ||
2946 | + new ParameterizedTypeReference<OtaPackageInfo>() {}, | ||
2947 | + params | ||
2948 | + ).getBody(); | ||
2949 | + } | ||
2950 | + | ||
2951 | + public OtaPackage getOtaPackageById(OtaPackageId otaPackageId) { | ||
2952 | + Map<String, String> params = new HashMap<>(); | ||
2953 | + params.put("otaPackageId", otaPackageId.getId().toString()); | ||
2954 | + | ||
2955 | + return restTemplate.exchange( | ||
2956 | + baseURL + "/api/otaPackage/{otaPackageId}", | ||
2957 | + HttpMethod.GET, | ||
2958 | + HttpEntity.EMPTY, | ||
2959 | + new ParameterizedTypeReference<OtaPackage>() {}, | ||
2960 | + params | ||
2961 | + ).getBody(); | ||
2962 | + } | ||
2963 | + | ||
2964 | + public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { | ||
2965 | + return restTemplate.postForEntity(baseURL + "/api/otaPackage", otaPackageInfo, OtaPackageInfo.class).getBody(); | ||
2966 | + } | ||
2967 | + | ||
2968 | + public OtaPackage saveOtaPackageData(OtaPackageId otaPackageId, String checkSum, ChecksumAlgorithm checksumAlgorithm, MultipartFile file) throws Exception { | ||
2969 | + HttpHeaders header = new HttpHeaders(); | ||
2970 | + header.setContentType(MediaType.MULTIPART_FORM_DATA); | ||
2971 | + | ||
2972 | + MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>(); | ||
2973 | + fileMap.add(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=" + file.getName()); | ||
2974 | + HttpEntity<ByteArrayResource> fileEntity = new HttpEntity<>(new ByteArrayResource(file.getBytes()), fileMap); | ||
2975 | + | ||
2976 | + MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); | ||
2977 | + body.add("file", fileEntity); | ||
2978 | + HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, header); | ||
2979 | + | ||
2980 | + Map<String, String> params = new HashMap<>(); | ||
2981 | + params.put("otaPackageId", otaPackageId.getId().toString()); | ||
2982 | + params.put("checksumAlgorithm", checksumAlgorithm.name()); | ||
2983 | + String url = "/api/otaPackage/{otaPackageId}?checksumAlgorithm={checksumAlgorithm}"; | ||
2984 | + | ||
2985 | + if(checkSum != null) { | ||
2986 | + url += "&checkSum={checkSum}"; | ||
2987 | + } | ||
2988 | + | ||
2989 | + return restTemplate.postForEntity( | ||
2990 | + baseURL + url, requestEntity, OtaPackage.class, params | ||
2991 | + ).getBody(); | ||
2992 | + } | ||
2993 | + | ||
2994 | + public PageData<OtaPackageInfo> getOtaPackages(PageLink pageLink) { | ||
2995 | + Map<String, String> params = new HashMap<>(); | ||
2996 | + addPageLinkToParam(params, pageLink); | ||
2997 | + | ||
2998 | + return restTemplate.exchange( | ||
2999 | + baseURL + "/api/otaPackages?" + getUrlParams(pageLink), | ||
3000 | + HttpMethod.GET, | ||
3001 | + HttpEntity.EMPTY, | ||
3002 | + new ParameterizedTypeReference<PageData<OtaPackageInfo>>() { | ||
3003 | + }, | ||
3004 | + params | ||
3005 | + ).getBody(); | ||
3006 | + } | ||
3007 | + | ||
3008 | + public PageData<OtaPackageInfo> getOtaPackages(DeviceProfileId deviceProfileId, | ||
3009 | + OtaPackageType otaPackageType, | ||
3010 | + boolean hasData, | ||
3011 | + PageLink pageLink) { | ||
3012 | + Map<String, String> params = new HashMap<>(); | ||
3013 | + params.put("hasData", String.valueOf(hasData)); | ||
3014 | + params.put("deviceProfileId", deviceProfileId.getId().toString()); | ||
3015 | + params.put("type", otaPackageType.name()); | ||
3016 | + addPageLinkToParam(params, pageLink); | ||
3017 | + | ||
3018 | + return restTemplate.exchange( | ||
3019 | + baseURL + "/api/otaPackages/{deviceProfileId}/{type}/{hasData}?" + getUrlParams(pageLink), | ||
3020 | + HttpMethod.GET, | ||
3021 | + HttpEntity.EMPTY, | ||
3022 | + new ParameterizedTypeReference<PageData<OtaPackageInfo>>() { | ||
3023 | + }, | ||
3024 | + params | ||
3025 | + ).getBody(); | ||
3026 | + } | ||
3027 | + | ||
3028 | + public void deleteOtaPackage(OtaPackageId otaPackageId) { | ||
3029 | + restTemplate.delete(baseURL + "/api/otaPackage/{otaPackageId}", otaPackageId.getId().toString()); | ||
3030 | + } | ||
3031 | + | ||
2833 | @Deprecated | 3032 | @Deprecated |
2834 | public Optional<JsonNode> getAttributes(String accessToken, String clientKeys, String sharedKeys) { | 3033 | public Optional<JsonNode> getAttributes(String accessToken, String clientKeys, String sharedKeys) { |
2835 | Map<String, String> params = new HashMap<>(); | 3034 | Map<String, String> params = new HashMap<>(); |
@@ -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(); |
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; | @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; | ||
29 | import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; | 29 | import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; |
30 | import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; | 30 | import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; |
31 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | 31 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
32 | +import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; | ||
32 | import org.thingsboard.server.common.data.id.DeviceId; | 33 | import org.thingsboard.server.common.data.id.DeviceId; |
33 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 34 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
34 | import org.thingsboard.server.common.data.id.EntityId; | 35 | import org.thingsboard.server.common.data.id.EntityId; |
@@ -150,6 +151,8 @@ class DeviceState { | @@ -150,6 +151,8 @@ class DeviceState { | ||
150 | stateChanged = processAlarmClearNotification(ctx, msg); | 151 | stateChanged = processAlarmClearNotification(ctx, msg); |
151 | } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { | 152 | } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { |
152 | processAlarmAckNotification(ctx, msg); | 153 | processAlarmAckNotification(ctx, msg); |
154 | + } else if (msg.getType().equals(DataConstants.ALARM_DELETE)) { | ||
155 | + processAlarmDeleteNotification(ctx, msg); | ||
153 | } else { | 156 | } else { |
154 | if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED) || msg.getType().equals(DataConstants.ENTITY_UNASSIGNED)) { | 157 | if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED) || msg.getType().equals(DataConstants.ENTITY_UNASSIGNED)) { |
155 | dynamicPredicateValueCtx.resetCustomer(); | 158 | dynamicPredicateValueCtx.resetCustomer(); |
@@ -193,6 +196,12 @@ class DeviceState { | @@ -193,6 +196,12 @@ class DeviceState { | ||
193 | ctx.tellSuccess(msg); | 196 | ctx.tellSuccess(msg); |
194 | } | 197 | } |
195 | 198 | ||
199 | + private void processAlarmDeleteNotification(TbContext ctx, TbMsg msg) { | ||
200 | + Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class); | ||
201 | + alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm().getId().equals(alarm.getId())); | ||
202 | + ctx.tellSuccess(msg); | ||
203 | + } | ||
204 | + | ||
196 | private boolean processAttributesUpdateNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { | 205 | private boolean processAttributesUpdateNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { |
197 | String scope = msg.getMetaData().getValue(DataConstants.SCOPE); | 206 | String scope = msg.getMetaData().getValue(DataConstants.SCOPE); |
198 | if (StringUtils.isEmpty(scope)) { | 207 | if (StringUtils.isEmpty(scope)) { |
@@ -253,7 +262,12 @@ class DeviceState { | @@ -253,7 +262,12 @@ class DeviceState { | ||
253 | for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { | 262 | for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { |
254 | AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), | 263 | AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), |
255 | a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx)); | 264 | a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx)); |
256 | - stateChanged |= alarmState.process(ctx, msg, latestValues, update); | 265 | + try { |
266 | + stateChanged |= alarmState.process(ctx, msg, latestValues, update); | ||
267 | + } catch (ApiUsageLimitsExceededException e) { | ||
268 | + alarmStates.remove(alarm.getId()); | ||
269 | + throw e; | ||
270 | + } | ||
257 | } | 271 | } |
258 | } | 272 | } |
259 | } | 273 | } |
@@ -152,6 +152,7 @@ export interface IStateController { | @@ -152,6 +152,7 @@ export interface IStateController { | ||
152 | getStateIndex(): number; | 152 | getStateIndex(): number; |
153 | getStateIdAtIndex(index: number): string; | 153 | getStateIdAtIndex(index: number): string; |
154 | getEntityId(entityParamName: string): EntityId; | 154 | getEntityId(entityParamName: string): EntityId; |
155 | + getCurrentStateName(): string; | ||
155 | } | 156 | } |
156 | 157 | ||
157 | export interface SubscriptionInfo { | 158 | export interface SubscriptionInfo { |
@@ -45,6 +45,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' | @@ -45,6 +45,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' | ||
45 | import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; | 45 | import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; |
46 | import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; | 46 | import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; |
47 | import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; | 47 | import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; |
48 | +import { isMobileApp } from '@core/utils'; | ||
48 | 49 | ||
49 | @Injectable({ | 50 | @Injectable({ |
50 | providedIn: 'root' | 51 | providedIn: 'root' |
@@ -194,11 +195,13 @@ export class AuthService { | @@ -194,11 +195,13 @@ export class AuthService { | ||
194 | } | 195 | } |
195 | 196 | ||
196 | public gotoDefaultPlace(isAuthenticated: boolean) { | 197 | public gotoDefaultPlace(isAuthenticated: boolean) { |
197 | - const authState = getCurrentAuthState(this.store); | ||
198 | - const url = this.defaultUrl(isAuthenticated, authState); | ||
199 | - this.zone.run(() => { | ||
200 | - this.router.navigateByUrl(url); | ||
201 | - }); | 198 | + if (!isMobileApp()) { |
199 | + const authState = getCurrentAuthState(this.store); | ||
200 | + const url = this.defaultUrl(isAuthenticated, authState); | ||
201 | + this.zone.run(() => { | ||
202 | + this.router.navigateByUrl(url); | ||
203 | + }); | ||
204 | + } | ||
202 | } | 205 | } |
203 | 206 | ||
204 | public loadOAuth2Clients(): Observable<Array<OAuth2ClientInfo>> { | 207 | public loadOAuth2Clients(): Observable<Array<OAuth2ClientInfo>> { |
@@ -516,12 +519,15 @@ export class AuthService { | @@ -516,12 +519,15 @@ export class AuthService { | ||
516 | return this.refreshTokenSubject !== null; | 519 | return this.refreshTokenSubject !== null; |
517 | } | 520 | } |
518 | 521 | ||
519 | - public setUserFromJwtToken(jwtToken, refreshToken, notify) { | 522 | + public setUserFromJwtToken(jwtToken, refreshToken, notify): Observable<boolean> { |
523 | + const authenticatedSubject = new ReplaySubject<boolean>(); | ||
520 | if (!jwtToken) { | 524 | if (!jwtToken) { |
521 | AuthService.clearTokenData(); | 525 | AuthService.clearTokenData(); |
522 | if (notify) { | 526 | if (notify) { |
523 | this.notifyUnauthenticated(); | 527 | this.notifyUnauthenticated(); |
524 | } | 528 | } |
529 | + authenticatedSubject.next(false); | ||
530 | + authenticatedSubject.complete(); | ||
525 | } else { | 531 | } else { |
526 | this.updateAndValidateTokens(jwtToken, refreshToken, true); | 532 | this.updateAndValidateTokens(jwtToken, refreshToken, true); |
527 | if (notify) { | 533 | if (notify) { |
@@ -530,16 +536,30 @@ export class AuthService { | @@ -530,16 +536,30 @@ export class AuthService { | ||
530 | (authPayload) => { | 536 | (authPayload) => { |
531 | this.notifyUserLoaded(true); | 537 | this.notifyUserLoaded(true); |
532 | this.notifyAuthenticated(authPayload); | 538 | this.notifyAuthenticated(authPayload); |
539 | + authenticatedSubject.next(true); | ||
540 | + authenticatedSubject.complete(); | ||
533 | }, | 541 | }, |
534 | () => { | 542 | () => { |
535 | this.notifyUserLoaded(true); | 543 | this.notifyUserLoaded(true); |
536 | this.notifyUnauthenticated(); | 544 | this.notifyUnauthenticated(); |
545 | + authenticatedSubject.next(false); | ||
546 | + authenticatedSubject.complete(); | ||
537 | } | 547 | } |
538 | ); | 548 | ); |
539 | } else { | 549 | } else { |
540 | - this.loadUser(false).subscribe(); | 550 | + this.loadUser(false).subscribe( |
551 | + () => { | ||
552 | + authenticatedSubject.next(true); | ||
553 | + authenticatedSubject.complete(); | ||
554 | + }, | ||
555 | + () => { | ||
556 | + authenticatedSubject.next(false); | ||
557 | + authenticatedSubject.complete(); | ||
558 | + } | ||
559 | + ); | ||
541 | } | 560 | } |
542 | } | 561 | } |
562 | + return authenticatedSubject; | ||
543 | } | 563 | } |
544 | 564 | ||
545 | private updateAndValidateTokens(jwtToken, refreshToken, notify: boolean) { | 565 | private updateAndValidateTokens(jwtToken, refreshToken, notify: boolean) { |
@@ -29,6 +29,7 @@ import { DialogService } from '@core/services/dialog.service'; | @@ -29,6 +29,7 @@ import { DialogService } from '@core/services/dialog.service'; | ||
29 | import { TranslateService } from '@ngx-translate/core'; | 29 | import { TranslateService } from '@ngx-translate/core'; |
30 | import { UtilsService } from '@core/services/utils.service'; | 30 | import { UtilsService } from '@core/services/utils.service'; |
31 | import { isObject } from '@core/utils'; | 31 | import { isObject } from '@core/utils'; |
32 | +import { MobileService } from '@core/services/mobile.service'; | ||
32 | 33 | ||
33 | @Injectable({ | 34 | @Injectable({ |
34 | providedIn: 'root' | 35 | providedIn: 'root' |
@@ -41,6 +42,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { | @@ -41,6 +42,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { | ||
41 | private dialogService: DialogService, | 42 | private dialogService: DialogService, |
42 | private utils: UtilsService, | 43 | private utils: UtilsService, |
43 | private translate: TranslateService, | 44 | private translate: TranslateService, |
45 | + private mobileService: MobileService, | ||
44 | private zone: NgZone) {} | 46 | private zone: NgZone) {} |
45 | 47 | ||
46 | getAuthState(): Observable<AuthState> { | 48 | getAuthState(): Observable<AuthState> { |
@@ -108,6 +110,10 @@ export class AuthGuard implements CanActivate, CanActivateChild { | @@ -108,6 +110,10 @@ export class AuthGuard implements CanActivate, CanActivateChild { | ||
108 | return of(false); | 110 | return of(false); |
109 | } | 111 | } |
110 | } | 112 | } |
113 | + if (this.mobileService.isMobileApp() && !path.startsWith('dashboard.')) { | ||
114 | + this.mobileService.handleMobileNavigation(path, params); | ||
115 | + return of(false); | ||
116 | + } | ||
111 | const defaultUrl = this.authService.defaultUrl(true, authState, path, params); | 117 | const defaultUrl = this.authService.defaultUrl(true, authState, path, params); |
112 | if (defaultUrl) { | 118 | if (defaultUrl) { |
113 | // this.authService.gotoDefaultPlace(true); | 119 | // this.authService.gotoDefaultPlace(true); |
@@ -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 |
@@ -20,9 +20,14 @@ import { isDefined } from '@core/utils'; | @@ -20,9 +20,14 @@ import { isDefined } from '@core/utils'; | ||
20 | import { MobileActionResult, WidgetMobileActionResult, WidgetMobileActionType } from '@shared/models/widget.models'; | 20 | import { MobileActionResult, WidgetMobileActionResult, WidgetMobileActionType } from '@shared/models/widget.models'; |
21 | import { from, of } from 'rxjs'; | 21 | import { from, of } from 'rxjs'; |
22 | import { Observable } from 'rxjs/internal/Observable'; | 22 | import { Observable } from 'rxjs/internal/Observable'; |
23 | -import { catchError } from 'rxjs/operators'; | 23 | +import { catchError, tap } from 'rxjs/operators'; |
24 | +import { OpenDashboardMessage, ReloadUserMessage, WindowMessage } from '@shared/models/window-message.model'; | ||
25 | +import { Params, Router } from '@angular/router'; | ||
26 | +import { AuthService } from '@core/auth/auth.service'; | ||
24 | 27 | ||
25 | const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; | 28 | const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; |
29 | +const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; | ||
30 | +const navigationHandler = 'tbMobileNavigationHandler'; | ||
26 | const mobileHandler = 'tbMobileHandler'; | 31 | const mobileHandler = 'tbMobileHandler'; |
27 | 32 | ||
28 | // @dynamic | 33 | // @dynamic |
@@ -34,10 +39,20 @@ export class MobileService { | @@ -34,10 +39,20 @@ export class MobileService { | ||
34 | private readonly mobileApp; | 39 | private readonly mobileApp; |
35 | private readonly mobileChannel; | 40 | private readonly mobileChannel; |
36 | 41 | ||
37 | - constructor(@Inject(WINDOW) private window: Window) { | 42 | + private readonly onWindowMessageListener = this.onWindowMessage.bind(this); |
43 | + | ||
44 | + private reloadUserObservable: Observable<boolean>; | ||
45 | + private lastDashboardId: string; | ||
46 | + | ||
47 | + constructor(@Inject(WINDOW) private window: Window, | ||
48 | + private router: Router, | ||
49 | + private authService: AuthService) { | ||
38 | const w = (this.window as any); | 50 | const w = (this.window as any); |
39 | this.mobileChannel = w.flutter_inappwebview; | 51 | this.mobileChannel = w.flutter_inappwebview; |
40 | this.mobileApp = isDefined(this.mobileChannel); | 52 | this.mobileApp = isDefined(this.mobileChannel); |
53 | + if (this.mobileApp) { | ||
54 | + window.addEventListener('message', this.onWindowMessageListener); | ||
55 | + } | ||
41 | } | 56 | } |
42 | 57 | ||
43 | public isMobileApp(): boolean { | 58 | public isMobileApp(): boolean { |
@@ -50,6 +65,12 @@ export class MobileService { | @@ -50,6 +65,12 @@ export class MobileService { | ||
50 | } | 65 | } |
51 | } | 66 | } |
52 | 67 | ||
68 | + public onDashboardLoaded() { | ||
69 | + if (this.mobileApp) { | ||
70 | + this.mobileChannel.callHandler(dashboardLoadedHandler); | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
53 | public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): | 74 | public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]): |
54 | Observable<WidgetMobileActionResult<T>> { | 75 | Observable<WidgetMobileActionResult<T>> { |
55 | if (this.mobileApp) { | 76 | if (this.mobileApp) { |
@@ -67,4 +88,82 @@ export class MobileService { | @@ -67,4 +88,82 @@ export class MobileService { | ||
67 | } | 88 | } |
68 | } | 89 | } |
69 | 90 | ||
91 | + public handleMobileNavigation(path?: string, params?: Params) { | ||
92 | + if (this.mobileApp) { | ||
93 | + this.mobileChannel.callHandler(navigationHandler, path, params); | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + private onWindowMessage(event: MessageEvent) { | ||
98 | + if (event.data) { | ||
99 | + let message: WindowMessage; | ||
100 | + try { | ||
101 | + message = JSON.parse(event.data); | ||
102 | + } catch (e) {} | ||
103 | + if (message && message.type) { | ||
104 | + switch (message.type) { | ||
105 | + case 'openDashboardMessage': | ||
106 | + const openDashboardMessage: OpenDashboardMessage = message.data; | ||
107 | + this.openDashboard(openDashboardMessage); | ||
108 | + break; | ||
109 | + case 'reloadUserMessage': | ||
110 | + const reloadUserMessage: ReloadUserMessage = message.data; | ||
111 | + this.reloadUser(reloadUserMessage); | ||
112 | + break; | ||
113 | + } | ||
114 | + } | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + private openDashboard(openDashboardMessage: OpenDashboardMessage) { | ||
119 | + if (openDashboardMessage && openDashboardMessage.dashboardId) { | ||
120 | + if (this.reloadUserObservable) { | ||
121 | + this.reloadUserObservable.subscribe( | ||
122 | + (authenticated) => { | ||
123 | + if (authenticated) { | ||
124 | + this.doDashboardNavigation(openDashboardMessage); | ||
125 | + } | ||
126 | + } | ||
127 | + ); | ||
128 | + } else { | ||
129 | + this.doDashboardNavigation(openDashboardMessage); | ||
130 | + } | ||
131 | + } | ||
132 | + } | ||
133 | + | ||
134 | + private doDashboardNavigation(openDashboardMessage: OpenDashboardMessage) { | ||
135 | + let url = `/dashboard/${openDashboardMessage.dashboardId}`; | ||
136 | + const params = []; | ||
137 | + if (openDashboardMessage.state) { | ||
138 | + params.push(`state=${openDashboardMessage.state}`); | ||
139 | + } | ||
140 | + if (openDashboardMessage.embedded) { | ||
141 | + params.push(`embedded=true`); | ||
142 | + } | ||
143 | + if (openDashboardMessage.hideToolbar) { | ||
144 | + params.push(`hideToolbar=true`); | ||
145 | + } | ||
146 | + if (this.lastDashboardId === openDashboardMessage.dashboardId) { | ||
147 | + params.push(`reload=${new Date().getTime()}`); | ||
148 | + } | ||
149 | + if (params.length) { | ||
150 | + url += `?${params.join('&')}`; | ||
151 | + } | ||
152 | + this.lastDashboardId = openDashboardMessage.dashboardId; | ||
153 | + this.router.navigateByUrl(url, {replaceUrl: true}); | ||
154 | + } | ||
155 | + | ||
156 | + private reloadUser(reloadUserMessage: ReloadUserMessage) { | ||
157 | + if (reloadUserMessage && reloadUserMessage.accessToken && reloadUserMessage.refreshToken) { | ||
158 | + this.reloadUserObservable = this.authService.setUserFromJwtToken(reloadUserMessage.accessToken, | ||
159 | + reloadUserMessage.refreshToken, true).pipe( | ||
160 | + tap( | ||
161 | + () => { | ||
162 | + this.reloadUserObservable = null; | ||
163 | + } | ||
164 | + ) | ||
165 | + ); | ||
166 | + } | ||
167 | + } | ||
168 | + | ||
70 | } | 169 | } |
@@ -441,3 +441,7 @@ export function generateSecret(length?: number): string { | @@ -441,3 +441,7 @@ export function generateSecret(length?: number): string { | ||
441 | export function validateEntityId(entityId: EntityId | null): boolean { | 441 | export function validateEntityId(entityId: EntityId | null): boolean { |
442 | return isDefinedAndNotNull(entityId?.id) && entityId.id !== NULL_UUID && isDefinedAndNotNull(entityId?.entityType); | 442 | return isDefinedAndNotNull(entityId?.id) && entityId.id !== NULL_UUID && isDefinedAndNotNull(entityId?.entityType); |
443 | } | 443 | } |
444 | + | ||
445 | +export function isMobileApp(): boolean { | ||
446 | + return isDefined((window as any).flutter_inappwebview); | ||
447 | +} |
@@ -87,7 +87,7 @@ | @@ -87,7 +87,7 @@ | ||
87 | (click)="updateDashboardImage($event)"> | 87 | (click)="updateDashboardImage($event)"> |
88 | <mat-icon>wallpaper</mat-icon> | 88 | <mat-icon>wallpaper</mat-icon> |
89 | </button> | 89 | </button> |
90 | - <button [fxShow]="currentDashboardId && (isEdit || displayExport())" mat-icon-button | 90 | + <button [fxShow]="currentDashboardId && !isMobileApp && (isEdit || displayExport())" mat-icon-button |
91 | matTooltip="{{'dashboard.export' | translate}}" | 91 | matTooltip="{{'dashboard.export' | translate}}" |
92 | matTooltipPosition="below" | 92 | matTooltipPosition="below" |
93 | (click)="exportDashboard($event)"> | 93 | (click)="exportDashboard($event)"> |
@@ -323,6 +323,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -323,6 +323,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
323 | this.runChangeDetection(); | 323 | this.runChangeDetection(); |
324 | } | 324 | } |
325 | )); | 325 | )); |
326 | + this.rxSubscriptions.push(this.route.queryParamMap.subscribe( | ||
327 | + (paramMap) => { | ||
328 | + if (paramMap.has('reload')) { | ||
329 | + this.dashboardCtx.aliasController.updateAliases(); | ||
330 | + setTimeout(() => { | ||
331 | + this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName()); | ||
332 | + this.mobileService.onDashboardLoaded(); | ||
333 | + }); | ||
334 | + } | ||
335 | + } | ||
336 | + )); | ||
326 | this.rxSubscriptions.push(this.breakpointObserver | 337 | this.rxSubscriptions.push(this.breakpointObserver |
327 | .observe(MediaBreakpoints['gt-sm']) | 338 | .observe(MediaBreakpoints['gt-sm']) |
328 | .subscribe((state: BreakpointState) => { | 339 | .subscribe((state: BreakpointState) => { |
@@ -770,6 +781,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | @@ -770,6 +781,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC | ||
770 | this.isRightLayoutOpened = openRightLayout ? true : false; | 781 | this.isRightLayoutOpened = openRightLayout ? true : false; |
771 | this.updateLayouts(layoutsData); | 782 | this.updateLayouts(layoutsData); |
772 | } | 783 | } |
784 | + setTimeout(() => { | ||
785 | + this.mobileService.onDashboardLoaded(); | ||
786 | + }); | ||
773 | } | 787 | } |
774 | 788 | ||
775 | private updateLayouts(layoutsData?: DashboardLayoutsInfo) { | 789 | private updateLayouts(layoutsData?: DashboardLayoutsInfo) { |
@@ -185,6 +185,10 @@ export class DefaultStateControllerComponent extends StateControllerComponent im | @@ -185,6 +185,10 @@ export class DefaultStateControllerComponent extends StateControllerComponent im | ||
185 | return this.utils.customTranslation(state.name, id); | 185 | return this.utils.customTranslation(state.name, id); |
186 | } | 186 | } |
187 | 187 | ||
188 | + public getCurrentStateName(): string { | ||
189 | + return this.getStateName(this.stateObject[0].id, this.statesValue[this.stateObject[0].id]); | ||
190 | + } | ||
191 | + | ||
188 | public displayStateSelection(): boolean { | 192 | public displayStateSelection(): boolean { |
189 | return this.states && Object.keys(this.states).length > 1; | 193 | return this.states && Object.keys(this.states).length > 1; |
190 | } | 194 | } |
@@ -235,6 +235,10 @@ export class EntityStateControllerComponent extends StateControllerComponent imp | @@ -235,6 +235,10 @@ export class EntityStateControllerComponent extends StateControllerComponent imp | ||
235 | return result; | 235 | return result; |
236 | } | 236 | } |
237 | 237 | ||
238 | + public getCurrentStateName(): string { | ||
239 | + return this.getStateName(this.stateObject.length - 1); | ||
240 | + } | ||
241 | + | ||
238 | public selectedStateIndexChanged() { | 242 | public selectedStateIndexChanged() { |
239 | this.navigatePrevState(this.selectedStateIndex); | 243 | this.navigatePrevState(this.selectedStateIndex); |
240 | } | 244 | } |
@@ -198,4 +198,6 @@ export abstract class StateControllerComponent implements IStateControllerCompon | @@ -198,4 +198,6 @@ export abstract class StateControllerComponent implements IStateControllerCompon | ||
198 | 198 | ||
199 | public abstract updateState(id?: string, params?: StateParams, openRightLayout?: boolean): void; | 199 | public abstract updateState(id?: string, params?: StateParams, openRightLayout?: boolean): void; |
200 | 200 | ||
201 | + public abstract getCurrentStateName(): string; | ||
202 | + | ||
201 | } | 203 | } |
@@ -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" |
@@ -161,6 +185,18 @@ | @@ -161,6 +185,18 @@ | ||
161 | </mat-error> | 185 | </mat-error> |
162 | </mat-form-field> | 186 | </mat-form-field> |
163 | <mat-form-field class="mat-block"> | 187 | <mat-form-field class="mat-block"> |
188 | + <mat-label translate>tenant-profile.alarms-ttl-days</mat-label> | ||
189 | + <input matInput required min="0" step="1" | ||
190 | + formControlName="alarmsTtlDays" | ||
191 | + type="number"> | ||
192 | + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('alarmsTtlDays').hasError('required')"> | ||
193 | + {{ 'tenant-profile.alarms-ttl-days-required' | translate}} | ||
194 | + </mat-error> | ||
195 | + <mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('alarmsTtlDays').hasError('min')"> | ||
196 | + {{ 'tenant-profile.alarms-ttl-days-days-range' | translate}} | ||
197 | + </mat-error> | ||
198 | + </mat-form-field> | ||
199 | + <mat-form-field class="mat-block"> | ||
164 | <mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label> | 200 | <mat-label translate>tenant-profile.max-rule-node-executions-per-message</mat-label> |
165 | <input matInput required min="0" step="1" | 201 | <input matInput required min="0" step="1" |
166 | formControlName="maxRuleNodeExecutionsPerMessage" | 202 | formControlName="maxRuleNodeExecutionsPerMessage" |
@@ -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, []], |
@@ -74,7 +76,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA | @@ -74,7 +76,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA | ||
74 | maxEmails: [null, [Validators.required, Validators.min(0)]], | 76 | maxEmails: [null, [Validators.required, Validators.min(0)]], |
75 | maxSms: [null, [Validators.required, Validators.min(0)]], | 77 | maxSms: [null, [Validators.required, Validators.min(0)]], |
76 | maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], | 78 | maxCreatedAlarms: [null, [Validators.required, Validators.min(0)]], |
77 | - defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]] | 79 | + defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], |
80 | + alarmsTtlDays: [null, [Validators.required, Validators.min(0)]] | ||
78 | }); | 81 | }); |
79 | this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { | 82 | this.defaultTenantProfileConfigurationFormGroup.valueChanges.subscribe(() => { |
80 | this.updateModel(); | 83 | this.updateModel(); |
@@ -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, |
@@ -14,9 +14,21 @@ | @@ -14,9 +14,21 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated'; | 17 | +export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage'; |
18 | 18 | ||
19 | export interface WindowMessage { | 19 | export interface WindowMessage { |
20 | type: WindowMessageType; | 20 | type: WindowMessageType; |
21 | data?: any; | 21 | data?: any; |
22 | } | 22 | } |
23 | + | ||
24 | +export interface OpenDashboardMessage { | ||
25 | + dashboardId: string; | ||
26 | + state?: string; | ||
27 | + hideToolbar?: boolean; | ||
28 | + embedded?: boolean; | ||
29 | +} | ||
30 | + | ||
31 | +export interface ReloadUserMessage { | ||
32 | + accessToken: string; | ||
33 | + refreshToken: string; | ||
34 | +} |
@@ -2504,6 +2504,12 @@ | @@ -2504,6 +2504,12 @@ | ||
2504 | "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)", | 2504 | "maximum-rule-chains": "Maximum number of rule chains (0 - unlimited)", |
2505 | "maximum-rule-chains-required": "Maximum number of rule chains is required.", | 2505 | "maximum-rule-chains-required": "Maximum number of rule chains is required.", |
2506 | "maximum-rule-chains-range": "Maximum number of rule chains can't be negative", | 2506 | "maximum-rule-chains-range": "Maximum number of rule chains can't be negative", |
2507 | + "maximum-resources-sum-data-size": "Maximum sum of resource files size in bytes (0 - unlimited)", | ||
2508 | + "maximum-resources-sum-data-size-required": "Maximum sum of resource files size is required.", | ||
2509 | + "maximum-resources-sum-data-size-range": "Maximum sum of resource files size can`t be negative", | ||
2510 | + "maximum-ota-packages-sum-data-size": "Maximum sum of ota package files size in bytes (0 - unlimited)", | ||
2511 | + "maximum-ota-package-sum-data-size-required": "Maximum sum of ota package files size is required.", | ||
2512 | + "maximum-ota-package-sum-data-size-range": "Maximum sum of ota package files size can`t be negative", | ||
2507 | "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", | 2513 | "transport-tenant-msg-rate-limit": "Transport tenant messages rate limit.", |
2508 | "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", | 2514 | "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages rate limit.", |
2509 | "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", | 2515 | "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points rate limit.", |
@@ -2528,6 +2534,9 @@ | @@ -2528,6 +2534,9 @@ | ||
2528 | "default-storage-ttl-days": "Default storage TTL days (0 - unlimited)", | 2534 | "default-storage-ttl-days": "Default storage TTL days (0 - unlimited)", |
2529 | "default-storage-ttl-days-required": "Default storage TTL days is required.", | 2535 | "default-storage-ttl-days-required": "Default storage TTL days is required.", |
2530 | "default-storage-ttl-days-range": "Default storage TTL days can't be negative", | 2536 | "default-storage-ttl-days-range": "Default storage TTL days can't be negative", |
2537 | + "alarms-ttl-days": "Alarms TTL days (0 - unlimited)", | ||
2538 | + "alarms-ttl-days-required": "Alarms TTL days required", | ||
2539 | + "alarms-ttl-days-days-range": "Alarms TTL days can't be negative", | ||
2531 | "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)", | 2540 | "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)", |
2532 | "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.", | 2541 | "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.", |
2533 | "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative", | 2542 | "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative", |
@@ -174,6 +174,48 @@ $tb-dark-theme: get-tb-dark-theme( | @@ -174,6 +174,48 @@ $tb-dark-theme: get-tb-dark-theme( | ||
174 | } | 174 | } |
175 | } | 175 | } |
176 | 176 | ||
177 | +@mixin _mat-toolbar-inverse-color($palette) { | ||
178 | + background: mat-color($palette, default-contrast); | ||
179 | + color: $dark-primary-text; | ||
180 | +} | ||
181 | + | ||
182 | +@mixin mat-fab-toolbar-inverse-theme($theme) { | ||
183 | + $primary: map-get($theme, primary); | ||
184 | + $accent: map-get($theme, accent); | ||
185 | + $warn: map-get($theme, warn); | ||
186 | + $background: map-get($theme, foreground); | ||
187 | + $foreground: map-get($theme, background); | ||
188 | + | ||
189 | + mat-fab-toolbar { | ||
190 | + .mat-fab-toolbar-background { | ||
191 | + background: mat-color($background, app-bar); | ||
192 | + color: mat-color($foreground, text); | ||
193 | + } | ||
194 | + &.mat-primary { | ||
195 | + .mat-fab-toolbar-background { | ||
196 | + @include _mat-toolbar-inverse-color($primary); | ||
197 | + } | ||
198 | + } | ||
199 | + mat-toolbar { | ||
200 | + &.mat-primary { | ||
201 | + @include _mat-toolbar-inverse-color($primary); | ||
202 | + button.mat-icon-button { | ||
203 | + mat-icon { | ||
204 | + color: mat-color($primary); | ||
205 | + } | ||
206 | + } | ||
207 | + } | ||
208 | + } | ||
209 | + .mat-fab { | ||
210 | + &.mat-primary { | ||
211 | + background: mat-color($primary, default-contrast); | ||
212 | + color: mat-color($primary); | ||
213 | + } | ||
214 | + } | ||
215 | + } | ||
216 | + | ||
217 | +} | ||
218 | + | ||
177 | @mixin tb-components-theme($theme) { | 219 | @mixin tb-components-theme($theme) { |
178 | $primary: map-get($theme, primary); | 220 | $primary: map-get($theme, primary); |
179 | 221 | ||
@@ -184,6 +226,10 @@ $tb-dark-theme: get-tb-dark-theme( | @@ -184,6 +226,10 @@ $tb-dark-theme: get-tb-dark-theme( | ||
184 | } | 226 | } |
185 | 227 | ||
186 | @include mat-fab-toolbar-theme($tb-theme); | 228 | @include mat-fab-toolbar-theme($tb-theme); |
229 | + | ||
230 | + div.tb-dashboard-page.mobile-app { | ||
231 | + @include mat-fab-toolbar-inverse-theme($tb-theme); | ||
232 | + } | ||
187 | } | 233 | } |
188 | 234 | ||
189 | .tb-default { | 235 | .tb-default { |
@@ -195,3 +241,4 @@ $tb-dark-theme: get-tb-dark-theme( | @@ -195,3 +241,4 @@ $tb-dark-theme: get-tb-dark-theme( | ||
195 | .tb-dark { | 241 | .tb-dark { |
196 | @include angular-material-theme($tb-dark-theme); | 242 | @include angular-material-theme($tb-dark-theme); |
197 | } | 243 | } |
244 | + |