Commit 8b1a9351f34717de9fc8f799fdee691a39ce9081
1 parent
88a171d7
Fixed for entity view afte testing
Showing
8 changed files
with
108 additions
and
90 deletions
... | ... | @@ -51,6 +51,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; |
51 | 51 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
52 | 52 | import org.thingsboard.server.dao.alarm.AlarmService; |
53 | 53 | import org.thingsboard.server.dao.asset.AssetService; |
54 | +import org.thingsboard.server.dao.attributes.AttributesService; | |
54 | 55 | import org.thingsboard.server.dao.audit.AuditLogService; |
55 | 56 | import org.thingsboard.server.dao.customer.CustomerService; |
56 | 57 | import org.thingsboard.server.dao.dashboard.DashboardService; |
... | ... | @@ -70,6 +71,7 @@ import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; |
70 | 71 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
71 | 72 | import org.thingsboard.server.service.security.model.SecurityUser; |
72 | 73 | import org.thingsboard.server.service.state.DeviceStateService; |
74 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
73 | 75 | |
74 | 76 | import javax.mail.MessagingException; |
75 | 77 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -143,6 +145,12 @@ public abstract class BaseController { |
143 | 145 | @Autowired |
144 | 146 | protected EntityViewService entityViewService; |
145 | 147 | |
148 | + @Autowired | |
149 | + protected TelemetrySubscriptionService tsSubService; | |
150 | + | |
151 | + @Autowired | |
152 | + protected AttributesService attributesService; | |
153 | + | |
146 | 154 | @ExceptionHandler(ThingsboardException.class) |
147 | 155 | public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { |
148 | 156 | errorResponseHandler.handle(ex, response); | ... | ... |
... | ... | @@ -15,7 +15,10 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import com.google.common.util.concurrent.Futures; | |
18 | 20 | import com.google.common.util.concurrent.ListenableFuture; |
21 | +import lombok.extern.slf4j.Slf4j; | |
19 | 22 | import org.springframework.http.HttpStatus; |
20 | 23 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | 24 | import org.springframework.web.bind.annotation.PathVariable; |
... | ... | @@ -26,7 +29,9 @@ import org.springframework.web.bind.annotation.RequestParam; |
26 | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
27 | 30 | import org.springframework.web.bind.annotation.ResponseStatus; |
28 | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | |
29 | 33 | import org.thingsboard.server.common.data.Customer; |
34 | +import org.thingsboard.server.common.data.DataConstants; | |
30 | 35 | import org.thingsboard.server.common.data.EntitySubtype; |
31 | 36 | import org.thingsboard.server.common.data.EntityType; |
32 | 37 | import org.thingsboard.server.common.data.EntityView; |
... | ... | @@ -34,15 +39,24 @@ import org.thingsboard.server.common.data.audit.ActionType; |
34 | 39 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
35 | 40 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
36 | 41 | import org.thingsboard.server.common.data.id.CustomerId; |
42 | +import org.thingsboard.server.common.data.id.DeviceId; | |
43 | +import org.thingsboard.server.common.data.id.EntityId; | |
37 | 44 | import org.thingsboard.server.common.data.id.EntityViewId; |
38 | 45 | import org.thingsboard.server.common.data.id.TenantId; |
46 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
47 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
39 | 48 | import org.thingsboard.server.common.data.page.TextPageData; |
40 | 49 | import org.thingsboard.server.common.data.page.TextPageLink; |
50 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | |
41 | 51 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
42 | 52 | import org.thingsboard.server.dao.model.ModelConstants; |
43 | 53 | import org.thingsboard.server.service.security.model.SecurityUser; |
44 | 54 | |
55 | +import javax.annotation.Nullable; | |
56 | +import java.util.ArrayList; | |
57 | +import java.util.Collection; | |
45 | 58 | import java.util.List; |
59 | +import java.util.concurrent.ExecutionException; | |
46 | 60 | import java.util.stream.Collectors; |
47 | 61 | |
48 | 62 | import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; |
... | ... | @@ -52,6 +66,7 @@ import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; |
52 | 66 | */ |
53 | 67 | @RestController |
54 | 68 | @RequestMapping("/api") |
69 | +@Slf4j | |
55 | 70 | public class EntityViewController extends BaseController { |
56 | 71 | |
57 | 72 | public static final String ENTITY_VIEW_ID = "entityViewId"; |
... | ... | @@ -75,6 +90,20 @@ public class EntityViewController extends BaseController { |
75 | 90 | try { |
76 | 91 | entityView.setTenantId(getCurrentUser().getTenantId()); |
77 | 92 | EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); |
93 | + List<ListenableFuture<List<Void>>> futures = new ArrayList<>(); | |
94 | + if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { | |
95 | + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); | |
96 | + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser())); | |
97 | + futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser())); | |
98 | + } | |
99 | + for (ListenableFuture<List<Void>> future : futures) { | |
100 | + try { | |
101 | + future.get(); | |
102 | + } catch (InterruptedException | ExecutionException e) { | |
103 | + throw new RuntimeException("Failed to copy attributes to entity view", e); | |
104 | + } | |
105 | + } | |
106 | + | |
78 | 107 | logEntityAction(savedEntityView.getId(), savedEntityView, null, |
79 | 108 | entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
80 | 109 | return savedEntityView; |
... | ... | @@ -85,6 +114,62 @@ public class EntityViewController extends BaseController { |
85 | 114 | } |
86 | 115 | } |
87 | 116 | |
117 | + private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, SecurityUser user) throws ThingsboardException { | |
118 | + EntityViewId entityId = entityView.getId(); | |
119 | + if (keys != null && !keys.isEmpty()) { | |
120 | + ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); | |
121 | + return Futures.transform(getAttrFuture, attributeKvEntries -> { | |
122 | + List<AttributeKvEntry> attributes; | |
123 | + if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { | |
124 | + attributes = | |
125 | + attributeKvEntries.stream() | |
126 | + .filter(attributeKvEntry -> { | |
127 | + long startTime = entityView.getStartTimeMs(); | |
128 | + long endTime = entityView.getEndTimeMs(); | |
129 | + long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); | |
130 | + return startTime == 0 && endTime == 0 || | |
131 | + (endTime == 0 && startTime < lastUpdateTs) || | |
132 | + (startTime == 0 && endTime > lastUpdateTs) | |
133 | + ? true : startTime < lastUpdateTs && endTime > lastUpdateTs; | |
134 | + }).collect(Collectors.toList()); | |
135 | + tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback<Void>() { | |
136 | + @Override | |
137 | + public void onSuccess(@Nullable Void tmp) { | |
138 | + try { | |
139 | + logAttributesUpdated(user, entityId, scope, attributes, null); | |
140 | + } catch (ThingsboardException e) { | |
141 | + log.error("Failed to log attribute updates", e); | |
142 | + } | |
143 | + if (entityId.getEntityType() == EntityType.ENTITY_VIEW) { | |
144 | + DeviceId deviceId = new DeviceId(entityId.getId()); | |
145 | + DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate( | |
146 | + user.getTenantId(), deviceId, scope, attributes); | |
147 | + actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg)); | |
148 | + } | |
149 | + } | |
150 | + | |
151 | + @Override | |
152 | + public void onFailure(Throwable t) { | |
153 | + try { | |
154 | + logAttributesUpdated(user, entityId, scope, attributes, t); | |
155 | + } catch (ThingsboardException e) { | |
156 | + log.error("Failed to log attribute updates", e); | |
157 | + } | |
158 | + } | |
159 | + }); | |
160 | + } | |
161 | + return null; | |
162 | + }); | |
163 | + } else { | |
164 | + return Futures.immediateFuture(null); | |
165 | + } | |
166 | + } | |
167 | + | |
168 | + private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List<AttributeKvEntry> attributes, Throwable e) throws ThingsboardException { | |
169 | + logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e), | |
170 | + scope, attributes); | |
171 | + } | |
172 | + | |
88 | 173 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
89 | 174 | @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE) |
90 | 175 | @ResponseStatus(value = HttpStatus.OK) | ... | ... |
... | ... | @@ -94,15 +94,9 @@ import java.util.stream.Collectors; |
94 | 94 | public class TelemetryController extends BaseController { |
95 | 95 | |
96 | 96 | @Autowired |
97 | - private AttributesService attributesService; | |
98 | - | |
99 | - @Autowired | |
100 | 97 | private TimeseriesService tsService; |
101 | 98 | |
102 | 99 | @Autowired |
103 | - private TelemetrySubscriptionService tsSubService; | |
104 | - | |
105 | - @Autowired | |
106 | 100 | private AccessValidator accessValidator; |
107 | 101 | |
108 | 102 | private ExecutorService executor; | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.asset.AssetService; |
29 | 29 | import org.thingsboard.server.dao.customer.CustomerService; |
30 | 30 | import org.thingsboard.server.dao.dashboard.DashboardService; |
31 | 31 | import org.thingsboard.server.dao.device.DeviceService; |
32 | +import org.thingsboard.server.dao.entityview.EntityViewService; | |
32 | 33 | import org.thingsboard.server.dao.rule.RuleChainService; |
33 | 34 | import org.thingsboard.server.dao.tenant.TenantService; |
34 | 35 | import org.thingsboard.server.dao.user.UserService; |
... | ... | @@ -47,6 +48,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe |
47 | 48 | private DeviceService deviceService; |
48 | 49 | |
49 | 50 | @Autowired |
51 | + private EntityViewService entityViewService; | |
52 | + | |
53 | + @Autowired | |
50 | 54 | private TenantService tenantService; |
51 | 55 | |
52 | 56 | @Autowired |
... | ... | @@ -81,6 +85,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe |
81 | 85 | case DEVICE: |
82 | 86 | hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); |
83 | 87 | break; |
88 | + case ENTITY_VIEW: | |
89 | + hasName = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId())); | |
90 | + break; | |
84 | 91 | case TENANT: |
85 | 92 | hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId())); |
86 | 93 | break; | ... | ... |
... | ... | @@ -105,21 +105,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti |
105 | 105 | log.trace("Executing save entity view [{}]", entityView); |
106 | 106 | entityViewValidator.validate(entityView); |
107 | 107 | EntityView savedEntityView = entityViewDao.save(entityView); |
108 | - | |
109 | - List<ListenableFuture<List<Void>>> futures = new ArrayList<>(); | |
110 | - if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { | |
111 | - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs())); | |
112 | - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs())); | |
113 | - futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh())); | |
114 | - } | |
115 | - for (ListenableFuture<List<Void>> future : futures) { | |
116 | - try { | |
117 | - future.get(); | |
118 | - } catch (InterruptedException | ExecutionException e) { | |
119 | - log.error("Failed to copy attributes to entity view", e); | |
120 | - throw new RuntimeException("Failed to copy attributes to entity view", e); | |
121 | - } | |
122 | - } | |
123 | 108 | return savedEntityView; |
124 | 109 | } |
125 | 110 | |
... | ... | @@ -294,36 +279,6 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti |
294 | 279 | }); |
295 | 280 | } |
296 | 281 | |
297 | - private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) { | |
298 | - if (keys != null && !keys.isEmpty()) { | |
299 | - ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); | |
300 | - return Futures.transform(getAttrFuture, attributeKvEntries -> { | |
301 | - List<AttributeKvEntry> filteredAttributes = new ArrayList<>(); | |
302 | - if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { | |
303 | - filteredAttributes = | |
304 | - attributeKvEntries.stream() | |
305 | - .filter(attributeKvEntry -> { | |
306 | - long startTime = entityView.getStartTimeMs(); | |
307 | - long endTime = entityView.getEndTimeMs(); | |
308 | - long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); | |
309 | - return startTime == 0 && endTime == 0 || | |
310 | - (endTime == 0 && startTime < lastUpdateTs) || | |
311 | - (startTime == 0 && endTime > lastUpdateTs) | |
312 | - ? true : startTime < lastUpdateTs && endTime > lastUpdateTs; | |
313 | - }).collect(Collectors.toList()); | |
314 | - } | |
315 | - try { | |
316 | - return attributesService.save(entityView.getId(), scope, filteredAttributes).get(); | |
317 | - } catch (InterruptedException | ExecutionException e) { | |
318 | - log.error("Failed to copy attributes to entity view", e); | |
319 | - throw new RuntimeException("Failed to copy attributes to entity view", e); | |
320 | - } | |
321 | - }); | |
322 | - } else { | |
323 | - return Futures.immediateFuture(null); | |
324 | - } | |
325 | - } | |
326 | - | |
327 | 282 | private DataValidator<EntityView> entityViewValidator = |
328 | 283 | new DataValidator<EntityView>() { |
329 | 284 | ... | ... |
... | ... | @@ -116,8 +116,10 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { |
116 | 116 | } |
117 | 117 | List<String> filteredAttributes = |
118 | 118 | attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr, entityView)).collect(Collectors.toList()); |
119 | - ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); | |
120 | - transformAndTellNext(ctx, msg, entityView); | |
119 | + if (filteredAttributes != null && !filteredAttributes.isEmpty()) { | |
120 | + ctx.getAttributesService().removeAll(entityView.getId(), scope, filteredAttributes); | |
121 | + transformAndTellNext(ctx, msg, entityView); | |
122 | + } | |
121 | 123 | } |
122 | 124 | } |
123 | 125 | } |
... | ... | @@ -139,19 +141,10 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { |
139 | 141 | private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) { |
140 | 142 | switch (scope) { |
141 | 143 | case DataConstants.CLIENT_SCOPE: |
142 | - if (entityView.getKeys().getAttributes().getCs().isEmpty()) { | |
143 | - return true; | |
144 | - } | |
145 | 144 | return entityView.getKeys().getAttributes().getCs().contains(attrKey); |
146 | 145 | case DataConstants.SERVER_SCOPE: |
147 | - if (entityView.getKeys().getAttributes().getSs().isEmpty()) { | |
148 | - return true; | |
149 | - } | |
150 | 146 | return entityView.getKeys().getAttributes().getSs().contains(attrKey); |
151 | 147 | case DataConstants.SHARED_SCOPE: |
152 | - if (entityView.getKeys().getAttributes().getSh().isEmpty()) { | |
153 | - return true; | |
154 | - } | |
155 | 148 | return entityView.getKeys().getAttributes().getSh().contains(attrKey); |
156 | 149 | } |
157 | 150 | return false; |
... | ... | @@ -159,6 +152,5 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { |
159 | 152 | |
160 | 153 | @Override |
161 | 154 | public void destroy() { |
162 | - | |
163 | 155 | } |
164 | 156 | } | ... | ... |
... | ... | @@ -27,7 +27,6 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu |
27 | 27 | deleteEntityView: deleteEntityView, |
28 | 28 | getCustomerEntityViews: getCustomerEntityViews, |
29 | 29 | getEntityView: getEntityView, |
30 | - getEntityViews: getEntityViews, | |
31 | 30 | getTenantEntityViews: getTenantEntityViews, |
32 | 31 | saveEntityView: saveEntityView, |
33 | 32 | unassignEntityViewFromCustomer: unassignEntityViewFromCustomer, |
... | ... | @@ -126,32 +125,6 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu |
126 | 125 | return deferred.promise; |
127 | 126 | } |
128 | 127 | |
129 | - function getEntityViews(entityViewIds, config) { | |
130 | - var deferred = $q.defer(); | |
131 | - var ids = ''; | |
132 | - for (var i=0;i<entityViewIds.length;i++) { | |
133 | - if (i>0) { | |
134 | - ids += ','; | |
135 | - } | |
136 | - ids += entityViewIds[i]; | |
137 | - } | |
138 | - var url = '/api/entityViews?entityViewIds=' + ids; | |
139 | - $http.get(url, config).then(function success(response) { | |
140 | - var entityViews = response.data; | |
141 | - entityViews.sort(function (entityView1, entityView2) { | |
142 | - var id1 = entityView1.id.id; | |
143 | - var id2 = entityView2.id.id; | |
144 | - var index1 = entityViewIds.indexOf(id1); | |
145 | - var index2 = entityViewIds.indexOf(id2); | |
146 | - return index1 - index2; | |
147 | - }); | |
148 | - deferred.resolve(entityViews); | |
149 | - }, function fail(response) { | |
150 | - deferred.reject(response.data); | |
151 | - }); | |
152 | - return deferred.promise; | |
153 | - } | |
154 | - | |
155 | 128 | function saveEntityView(entityView) { |
156 | 129 | var deferred = $q.defer(); |
157 | 130 | var url = '/api/entityView'; | ... | ... |
... | ... | @@ -135,6 +135,10 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
135 | 135 | case types.entityType.asset: |
136 | 136 | promise = assetService.getAssets(entityIds, config); |
137 | 137 | break; |
138 | + case types.entityType.entityView: | |
139 | + promise = getEntitiesByIdsPromise( | |
140 | + (id) => entityViewService.getEntityView(id, config), entityIds); | |
141 | + break; | |
138 | 142 | case types.entityType.tenant: |
139 | 143 | promise = getEntitiesByIdsPromise( |
140 | 144 | (id) => tenantService.getTenant(id, config), entityIds); | ... | ... |