Showing
9 changed files
with
91 additions
and
70 deletions
... | ... | @@ -120,7 +120,9 @@ public class AlarmController extends BaseController { |
120 | 120 | try { |
121 | 121 | AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); |
122 | 122 | Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); |
123 | - alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, System.currentTimeMillis()).get(); | |
123 | + long ackTs = System.currentTimeMillis(); | |
124 | + alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); | |
125 | + alarm.setAckTs(ackTs); | |
124 | 126 | logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); |
125 | 127 | } catch (Exception e) { |
126 | 128 | throw handleException(e); |
... | ... | @@ -135,7 +137,9 @@ public class AlarmController extends BaseController { |
135 | 137 | try { |
136 | 138 | AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); |
137 | 139 | Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); |
138 | - alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, System.currentTimeMillis()).get(); | |
140 | + long clearTs = System.currentTimeMillis(); | |
141 | + alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); | |
142 | + alarm.setClearTs(clearTs); | |
139 | 143 | logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); |
140 | 144 | } catch (Exception e) { |
141 | 145 | throw handleException(e); | ... | ... |
... | ... | @@ -24,13 +24,11 @@ import org.springframework.web.bind.annotation.RequestParam; |
24 | 24 | import org.springframework.web.bind.annotation.ResponseBody; |
25 | 25 | import org.springframework.web.bind.annotation.ResponseStatus; |
26 | 26 | import org.springframework.web.bind.annotation.RestController; |
27 | -import org.thingsboard.server.common.data.alarm.Alarm; | |
28 | 27 | import org.thingsboard.server.common.data.audit.ActionType; |
29 | 28 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
30 | 29 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
31 | 30 | import org.thingsboard.server.common.data.id.EntityId; |
32 | 31 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
33 | -import org.thingsboard.server.common.data.id.UUIDBased; | |
34 | 32 | import org.thingsboard.server.common.data.relation.EntityRelation; |
35 | 33 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; |
36 | 34 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
... | ... | @@ -38,6 +36,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
38 | 36 | import org.thingsboard.server.service.security.permission.Operation; |
39 | 37 | |
40 | 38 | import java.util.List; |
39 | +import java.util.stream.Collectors; | |
41 | 40 | |
42 | 41 | |
43 | 42 | @RestController |
... | ... | @@ -167,7 +166,7 @@ public class EntityRelationController extends BaseController { |
167 | 166 | checkEntityId(entityId, Operation.READ); |
168 | 167 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
169 | 168 | try { |
170 | - return checkNotNull(relationService.findByFrom(getTenantId(), entityId, typeGroup)); | |
169 | + return checkNotNull(filterRelationsByReadPermission(relationService.findByFrom(getTenantId(), entityId, typeGroup))); | |
171 | 170 | } catch (Exception e) { |
172 | 171 | throw handleException(e); |
173 | 172 | } |
... | ... | @@ -185,7 +184,7 @@ public class EntityRelationController extends BaseController { |
185 | 184 | checkEntityId(entityId, Operation.READ); |
186 | 185 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
187 | 186 | try { |
188 | - return checkNotNull(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get()); | |
187 | + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByFrom(getTenantId(), entityId, typeGroup).get())); | |
189 | 188 | } catch (Exception e) { |
190 | 189 | throw handleException(e); |
191 | 190 | } |
... | ... | @@ -205,7 +204,7 @@ public class EntityRelationController extends BaseController { |
205 | 204 | checkEntityId(entityId, Operation.READ); |
206 | 205 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
207 | 206 | try { |
208 | - return checkNotNull(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup)); | |
207 | + return checkNotNull(filterRelationsByReadPermission(relationService.findByFromAndType(getTenantId(), entityId, strRelationType, typeGroup))); | |
209 | 208 | } catch (Exception e) { |
210 | 209 | throw handleException(e); |
211 | 210 | } |
... | ... | @@ -223,7 +222,7 @@ public class EntityRelationController extends BaseController { |
223 | 222 | checkEntityId(entityId, Operation.READ); |
224 | 223 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
225 | 224 | try { |
226 | - return checkNotNull(relationService.findByTo(getTenantId(), entityId, typeGroup)); | |
225 | + return checkNotNull(filterRelationsByReadPermission(relationService.findByTo(getTenantId(), entityId, typeGroup))); | |
227 | 226 | } catch (Exception e) { |
228 | 227 | throw handleException(e); |
229 | 228 | } |
... | ... | @@ -241,7 +240,7 @@ public class EntityRelationController extends BaseController { |
241 | 240 | checkEntityId(entityId, Operation.READ); |
242 | 241 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
243 | 242 | try { |
244 | - return checkNotNull(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get()); | |
243 | + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByTo(getTenantId(), entityId, typeGroup).get())); | |
245 | 244 | } catch (Exception e) { |
246 | 245 | throw handleException(e); |
247 | 246 | } |
... | ... | @@ -261,7 +260,7 @@ public class EntityRelationController extends BaseController { |
261 | 260 | checkEntityId(entityId, Operation.READ); |
262 | 261 | RelationTypeGroup typeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); |
263 | 262 | try { |
264 | - return checkNotNull(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup)); | |
263 | + return checkNotNull(filterRelationsByReadPermission(relationService.findByToAndType(getTenantId(), entityId, strRelationType, typeGroup))); | |
265 | 264 | } catch (Exception e) { |
266 | 265 | throw handleException(e); |
267 | 266 | } |
... | ... | @@ -276,7 +275,7 @@ public class EntityRelationController extends BaseController { |
276 | 275 | checkNotNull(query.getFilters()); |
277 | 276 | checkEntityId(query.getParameters().getEntityId(), Operation.READ); |
278 | 277 | try { |
279 | - return checkNotNull(relationService.findByQuery(getTenantId(), query).get()); | |
278 | + return checkNotNull(filterRelationsByReadPermission(relationService.findByQuery(getTenantId(), query).get())); | |
280 | 279 | } catch (Exception e) { |
281 | 280 | throw handleException(e); |
282 | 281 | } |
... | ... | @@ -291,12 +290,28 @@ public class EntityRelationController extends BaseController { |
291 | 290 | checkNotNull(query.getFilters()); |
292 | 291 | checkEntityId(query.getParameters().getEntityId(), Operation.READ); |
293 | 292 | try { |
294 | - return checkNotNull(relationService.findInfoByQuery(getTenantId(), query).get()); | |
293 | + return checkNotNull(filterRelationsByReadPermission(relationService.findInfoByQuery(getTenantId(), query).get())); | |
295 | 294 | } catch (Exception e) { |
296 | 295 | throw handleException(e); |
297 | 296 | } |
298 | 297 | } |
299 | 298 | |
299 | + private <T extends EntityRelation> List<T> filterRelationsByReadPermission(List<T> relationsByQuery) { | |
300 | + return relationsByQuery.stream().filter(relationByQuery -> { | |
301 | + try { | |
302 | + checkEntityId(relationByQuery.getTo(), Operation.READ); | |
303 | + } catch (ThingsboardException e) { | |
304 | + return false; | |
305 | + } | |
306 | + try { | |
307 | + checkEntityId(relationByQuery.getFrom(), Operation.READ); | |
308 | + } catch (ThingsboardException e) { | |
309 | + return false; | |
310 | + } | |
311 | + return true; | |
312 | + }).collect(Collectors.toList()); | |
313 | + } | |
314 | + | |
300 | 315 | private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { |
301 | 316 | RelationTypeGroup result = defaultValue; |
302 | 317 | if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length() > 0) { | ... | ... |
... | ... | @@ -39,7 +39,7 @@ public interface AlarmService { |
39 | 39 | |
40 | 40 | ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); |
41 | 41 | |
42 | - ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long ackTs); | |
42 | + ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); | |
43 | 43 | |
44 | 44 | ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId); |
45 | 45 | ... | ... |
... | ... | @@ -407,8 +407,12 @@ final class MqttClientImpl implements MqttClient { |
407 | 407 | } |
408 | 408 | |
409 | 409 | private MqttMessageIdVariableHeader getNewMessageId() { |
410 | - this.nextMessageId.compareAndSet(0xffff, 1); | |
411 | - return MqttMessageIdVariableHeader.from(this.nextMessageId.getAndIncrement()); | |
410 | + int messageId; | |
411 | + synchronized (this.nextMessageId) { | |
412 | + this.nextMessageId.compareAndSet(0xffff, 1); | |
413 | + messageId = this.nextMessageId.getAndIncrement(); | |
414 | + } | |
415 | + return MqttMessageIdVariableHeader.from(messageId); | |
412 | 416 | } |
413 | 417 | |
414 | 418 | private Future<Void> createSubscription(String topic, MqttHandler handler, boolean once, MqttQoS qos) { | ... | ... |
... | ... | @@ -71,7 +71,7 @@ while : |
71 | 71 | echo "Done" |
72 | 72 | exit 0 |
73 | 73 | ;; |
74 | - [yY]|[yY][eE]|[yY][eE]|[sS]|[yY]|"") | |
74 | + [yY]|[yY][eE]|[yY][eE][sS]|"") | |
75 | 75 | echo "Cleaning up files" |
76 | 76 | rm -rf $SERVER_FILE_PREFIX.jks |
77 | 77 | rm -rf $SERVER_FILE_PREFIX.pub.pem |
... | ... | @@ -129,7 +129,7 @@ if [[ $COPY = true ]]; then |
129 | 129 | [nN]|[nN][oO]) |
130 | 130 | break |
131 | 131 | ;; |
132 | - [yY]|[yY][eE]|[yY][eE]|[sS]|[yY]|"") | |
132 | + [yY]|[yY][eE]|[yY][eE][sS]|"") | |
133 | 133 | read -p "(Default: $SERVER_KEYSTORE_DIR): " dir |
134 | 134 | if [[ ! -z $dir ]]; then |
135 | 135 | DESTINATION=$dir; | ... | ... |
... | ... | @@ -616,7 +616,7 @@ export default class TbMapWidgetV2 { |
616 | 616 | locationChanged = true; |
617 | 617 | } |
618 | 618 | } |
619 | - } else { | |
619 | + } else if (latData.length > 0 && lngData.length > 0) { | |
620 | 620 | // Create or update marker |
621 | 621 | lat = latData[latData.length - 1][1]; |
622 | 622 | lng = lngData[lngData.length - 1][1]; | ... | ... |
... | ... | @@ -38,56 +38,58 @@ |
38 | 38 | </div> |
39 | 39 | </md-toolbar> |
40 | 40 | |
41 | - <md-tabs flex md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}" | |
41 | + <md-tabs md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}" | |
42 | 42 | id="tabs" md-border-bottom flex> |
43 | 43 | <md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}"> |
44 | - <md-table-container class="flex"> | |
45 | - <table md-table> | |
46 | - <thead fix-head md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)"> | |
47 | - <tr md-row> | |
48 | - <th ng-show="vm.showTimestamp" | |
49 | - md-column md-order-by="0" | |
50 | - > | |
51 | - <span>Timestamp</span> | |
52 | - </th> | |
53 | - <th md-column | |
54 | - md-order-by="{{ h.index }}" | |
55 | - ng-repeat="h in source.ts.header" | |
56 | - > | |
57 | - <span>{{ h.dataKey.label }}</span> | |
58 | - </th> | |
59 | - </tr> | |
60 | - </thead> | |
44 | + <div class="tb-absolute-fill" layout="column"> | |
45 | + <md-table-container flex> | |
46 | + <table md-table> | |
47 | + <thead fix-head md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)"> | |
48 | + <tr md-row> | |
49 | + <th ng-show="vm.showTimestamp" | |
50 | + md-column md-order-by="0" | |
51 | + > | |
52 | + <span>Timestamp</span> | |
53 | + </th> | |
54 | + <th md-column | |
55 | + md-order-by="{{ h.index }}" | |
56 | + ng-repeat="h in source.ts.header" | |
57 | + > | |
58 | + <span>{{ h.dataKey.label }}</span> | |
59 | + </th> | |
60 | + </tr> | |
61 | + </thead> | |
61 | 62 | |
62 | - <tbody md-body> | |
63 | - <tr md-row ng-repeat="row in source.ts.data track by $index" ng-click="vm.onRowClick($event, row)"> | |
64 | - <td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)" | |
65 | - md-cell | |
66 | - ng-repeat="d in row track by $index" | |
67 | - ng-style="vm.cellStyle(source, $index, d)" | |
68 | - ng-bind-html="vm.cellContent(source, $index, row, d)" | |
69 | - ></td> | |
70 | - <td md-cell class="tb-action-cell" | |
71 | - ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px', | |
72 | - maxWidth: vm.actionCellDescriptors.length*36+'px', | |
73 | - width: vm.actionCellDescriptors.length*36+'px'}"> | |
74 | - <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors" | |
75 | - aria-label="{{ actionDescriptor.displayName }}" | |
76 | - ng-click="vm.onActionButtonClick($event, row, actionDescriptor)" ng-disabled="$root.loading"> | |
77 | - <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon> | |
78 | - <md-tooltip md-direction="top"> | |
79 | - {{ actionDescriptor.displayName }} | |
80 | - </md-tooltip> | |
81 | - </md-button> | |
82 | - </td> | |
83 | - </tr> | |
84 | - </tbody> | |
85 | - </table> | |
86 | - <md-divider></md-divider> | |
87 | - <span ng-show="!vm.sources[vm.sourceIndex].data.length" | |
88 | - layout-align="center center" | |
89 | - class="no-data-found" translate>widget.no-data-found</span> | |
90 | - </md-table-container> | |
63 | + <tbody md-body> | |
64 | + <tr md-row ng-repeat="row in source.ts.data track by $index" ng-click="vm.onRowClick($event, row)"> | |
65 | + <td ng-show="$index > 0 || ($index === 0 && vm.showTimestamp)" | |
66 | + md-cell | |
67 | + ng-repeat="d in row track by $index" | |
68 | + ng-style="vm.cellStyle(source, $index, d)" | |
69 | + ng-bind-html="vm.cellContent(source, $index, row, d)" | |
70 | + ></td> | |
71 | + <td md-cell class="tb-action-cell" | |
72 | + ng-style="{minWidth: vm.actionCellDescriptors.length*36+'px', | |
73 | + maxWidth: vm.actionCellDescriptors.length*36+'px', | |
74 | + width: vm.actionCellDescriptors.length*36+'px'}"> | |
75 | + <md-button class="md-icon-button" ng-repeat="actionDescriptor in vm.actionCellDescriptors" | |
76 | + aria-label="{{ actionDescriptor.displayName }}" | |
77 | + ng-click="vm.onActionButtonClick($event, row, actionDescriptor)" ng-disabled="$root.loading"> | |
78 | + <md-icon aria-label="{{ actionDescriptor.displayName }}" class="material-icons">{{actionDescriptor.icon}}</md-icon> | |
79 | + <md-tooltip md-direction="top"> | |
80 | + {{ actionDescriptor.displayName }} | |
81 | + </md-tooltip> | |
82 | + </md-button> | |
83 | + </td> | |
84 | + </tr> | |
85 | + </tbody> | |
86 | + </table> | |
87 | + <md-divider></md-divider> | |
88 | + <span ng-show="!vm.sources[vm.sourceIndex].data.length" | |
89 | + layout-align="center center" | |
90 | + class="no-data-found" translate>widget.no-data-found</span> | |
91 | + </md-table-container> | |
92 | + </div> | |
91 | 93 | </md-tab> |
92 | 94 | </md-tabs> |
93 | 95 | <md-table-pagination ng-if="vm.displayPagination" | ... | ... |