|
|
|
@ -15,7 +15,10 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.controller; |
|
|
|
|
|
|
|
import com.google.common.util.concurrent.FutureCallback; |
|
|
|
import com.google.common.util.concurrent.Futures; |
|
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.http.HttpStatus; |
|
|
|
import org.springframework.security.access.prepost.PreAuthorize; |
|
|
|
import org.springframework.web.bind.annotation.PathVariable; |
|
|
|
@ -26,7 +29,9 @@ import org.springframework.web.bind.annotation.RequestParam; |
|
|
|
import org.springframework.web.bind.annotation.ResponseBody; |
|
|
|
import org.springframework.web.bind.annotation.ResponseStatus; |
|
|
|
import org.springframework.web.bind.annotation.RestController; |
|
|
|
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
|
|
|
import org.thingsboard.server.common.data.Customer; |
|
|
|
import org.thingsboard.server.common.data.DataConstants; |
|
|
|
import org.thingsboard.server.common.data.EntitySubtype; |
|
|
|
import org.thingsboard.server.common.data.EntityType; |
|
|
|
import org.thingsboard.server.common.data.EntityView; |
|
|
|
@ -34,15 +39,24 @@ import org.thingsboard.server.common.data.audit.ActionType; |
|
|
|
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
|
|
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceId; |
|
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
|
import org.thingsboard.server.common.data.id.EntityViewId; |
|
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
|
import org.thingsboard.server.common.data.id.UUIDBased; |
|
|
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
|
|
|
import org.thingsboard.server.common.data.page.TextPageData; |
|
|
|
import org.thingsboard.server.common.data.page.TextPageLink; |
|
|
|
import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
|
|
|
import org.thingsboard.server.dao.exception.IncorrectParameterException; |
|
|
|
import org.thingsboard.server.dao.model.ModelConstants; |
|
|
|
import org.thingsboard.server.service.security.model.SecurityUser; |
|
|
|
|
|
|
|
import javax.annotation.Nullable; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.List; |
|
|
|
import java.util.concurrent.ExecutionException; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; |
|
|
|
@ -52,6 +66,7 @@ import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; |
|
|
|
*/ |
|
|
|
@RestController |
|
|
|
@RequestMapping("/api") |
|
|
|
@Slf4j |
|
|
|
public class EntityViewController extends BaseController { |
|
|
|
|
|
|
|
public static final String ENTITY_VIEW_ID = "entityViewId"; |
|
|
|
@ -75,6 +90,20 @@ public class EntityViewController extends BaseController { |
|
|
|
try { |
|
|
|
entityView.setTenantId(getCurrentUser().getTenantId()); |
|
|
|
EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); |
|
|
|
List<ListenableFuture<List<Void>>> futures = new ArrayList<>(); |
|
|
|
if (savedEntityView.getKeys() != null && savedEntityView.getKeys().getAttributes() != null) { |
|
|
|
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.CLIENT_SCOPE, savedEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); |
|
|
|
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SERVER_SCOPE, savedEntityView.getKeys().getAttributes().getSs(), getCurrentUser())); |
|
|
|
futures.add(copyAttributesFromEntityToEntityView(savedEntityView, DataConstants.SHARED_SCOPE, savedEntityView.getKeys().getAttributes().getSh(), getCurrentUser())); |
|
|
|
} |
|
|
|
for (ListenableFuture<List<Void>> future : futures) { |
|
|
|
try { |
|
|
|
future.get(); |
|
|
|
} catch (InterruptedException | ExecutionException e) { |
|
|
|
throw new RuntimeException("Failed to copy attributes to entity view", e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
logEntityAction(savedEntityView.getId(), savedEntityView, null, |
|
|
|
entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
|
|
|
return savedEntityView; |
|
|
|
@ -85,6 +114,62 @@ public class EntityViewController extends BaseController { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys, SecurityUser user) throws ThingsboardException { |
|
|
|
EntityViewId entityId = entityView.getId(); |
|
|
|
if (keys != null && !keys.isEmpty()) { |
|
|
|
ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); |
|
|
|
return Futures.transform(getAttrFuture, attributeKvEntries -> { |
|
|
|
List<AttributeKvEntry> attributes; |
|
|
|
if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { |
|
|
|
attributes = |
|
|
|
attributeKvEntries.stream() |
|
|
|
.filter(attributeKvEntry -> { |
|
|
|
long startTime = entityView.getStartTimeMs(); |
|
|
|
long endTime = entityView.getEndTimeMs(); |
|
|
|
long lastUpdateTs = attributeKvEntry.getLastUpdateTs(); |
|
|
|
return startTime == 0 && endTime == 0 || |
|
|
|
(endTime == 0 && startTime < lastUpdateTs) || |
|
|
|
(startTime == 0 && endTime > lastUpdateTs) |
|
|
|
? true : startTime < lastUpdateTs && endTime > lastUpdateTs; |
|
|
|
}).collect(Collectors.toList()); |
|
|
|
tsSubService.saveAndNotify(entityId, scope, attributes, new FutureCallback<Void>() { |
|
|
|
@Override |
|
|
|
public void onSuccess(@Nullable Void tmp) { |
|
|
|
try { |
|
|
|
logAttributesUpdated(user, entityId, scope, attributes, null); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log attribute updates", e); |
|
|
|
} |
|
|
|
if (entityId.getEntityType() == EntityType.ENTITY_VIEW) { |
|
|
|
DeviceId deviceId = new DeviceId(entityId.getId()); |
|
|
|
DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate( |
|
|
|
user.getTenantId(), deviceId, scope, attributes); |
|
|
|
actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onFailure(Throwable t) { |
|
|
|
try { |
|
|
|
logAttributesUpdated(user, entityId, scope, attributes, t); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log attribute updates", e); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
return null; |
|
|
|
}); |
|
|
|
} else { |
|
|
|
return Futures.immediateFuture(null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List<AttributeKvEntry> attributes, Throwable e) throws ThingsboardException { |
|
|
|
logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e), |
|
|
|
scope, attributes); |
|
|
|
} |
|
|
|
|
|
|
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
|
|
|
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE) |
|
|
|
@ResponseStatus(value = HttpStatus.OK) |
|
|
|
|