|
|
|
@ -19,7 +19,9 @@ import com.google.common.util.concurrent.FutureCallback; |
|
|
|
import com.google.common.util.concurrent.Futures; |
|
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
|
import com.google.common.util.concurrent.MoreExecutors; |
|
|
|
import com.google.common.util.concurrent.SettableFuture; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.http.HttpStatus; |
|
|
|
import org.springframework.security.access.prepost.PreAuthorize; |
|
|
|
import org.springframework.web.bind.annotation.PathVariable; |
|
|
|
@ -40,10 +42,14 @@ 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.kv.BaseReadTsKvQuery; |
|
|
|
import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
|
|
|
import org.thingsboard.server.common.data.kv.TsKvEntry; |
|
|
|
import org.thingsboard.server.common.data.page.PageData; |
|
|
|
import org.thingsboard.server.common.data.page.PageLink; |
|
|
|
import org.thingsboard.server.dao.exception.IncorrectParameterException; |
|
|
|
import org.thingsboard.server.dao.model.ModelConstants; |
|
|
|
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|
|
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
|
|
|
import org.thingsboard.server.service.security.model.SecurityUser; |
|
|
|
import org.thingsboard.server.service.security.permission.Operation; |
|
|
|
@ -69,6 +75,9 @@ public class EntityViewController extends BaseController { |
|
|
|
|
|
|
|
public static final String ENTITY_VIEW_ID = "entityViewId"; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private TimeseriesService tsService; |
|
|
|
|
|
|
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
|
|
|
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET) |
|
|
|
@ResponseBody |
|
|
|
@ -101,16 +110,37 @@ public class EntityViewController extends BaseController { |
|
|
|
try { |
|
|
|
entityView.setTenantId(getCurrentUser().getTenantId()); |
|
|
|
|
|
|
|
checkEntity(entityView.getId(), entityView, Resource.ENTITY_VIEW); |
|
|
|
List<ListenableFuture<?>> futures = new ArrayList<>(); |
|
|
|
|
|
|
|
if (entityView.getId() == null) { |
|
|
|
accessControlService |
|
|
|
.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.CREATE, null, entityView); |
|
|
|
} else { |
|
|
|
EntityView existingEntityView = checkNotNull(entityViewService.findEntityViewById(getCurrentUser().getTenantId(), entityView.getId())); |
|
|
|
if (existingEntityView.getKeys() != null) { |
|
|
|
if (existingEntityView.getKeys().getAttributes() != null) { |
|
|
|
futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.CLIENT_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); |
|
|
|
futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SERVER_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); |
|
|
|
futures.add(deleteAttributesFromEntityView(existingEntityView, DataConstants.SHARED_SCOPE, existingEntityView.getKeys().getAttributes().getCs(), getCurrentUser())); |
|
|
|
} |
|
|
|
if (existingEntityView.getKeys().getTimeseries() != null && !existingEntityView.getKeys().getTimeseries().isEmpty()) { |
|
|
|
futures.add(deleteLatestFromEntityView(existingEntityView, existingEntityView.getKeys().getTimeseries(), getCurrentUser())); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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())); |
|
|
|
if (savedEntityView.getKeys() != null) { |
|
|
|
if (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())); |
|
|
|
} |
|
|
|
if (savedEntityView.getKeys().getTimeseries() != null && !savedEntityView.getKeys().getTimeseries().isEmpty()) { |
|
|
|
futures.add(copyLatestFromEntityToEntityView(savedEntityView, getCurrentUser())); |
|
|
|
} |
|
|
|
} |
|
|
|
for (ListenableFuture<List<Void>> future : futures) { |
|
|
|
for (ListenableFuture<?> future : futures) { |
|
|
|
try { |
|
|
|
future.get(); |
|
|
|
} catch (InterruptedException | ExecutionException e) { |
|
|
|
@ -128,6 +158,98 @@ public class EntityViewController extends BaseController { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private ListenableFuture<Void> deleteLatestFromEntityView(EntityView entityView, List<String> keys, SecurityUser user) { |
|
|
|
EntityViewId entityId = entityView.getId(); |
|
|
|
SettableFuture<Void> resultFuture = SettableFuture.create(); |
|
|
|
if (keys != null && !keys.isEmpty()) { |
|
|
|
tsSubService.deleteLatest(entityView.getTenantId(), entityId, keys, new FutureCallback<Void>() { |
|
|
|
@Override |
|
|
|
public void onSuccess(@Nullable Void tmp) { |
|
|
|
try { |
|
|
|
logTimeseriesDeleted(user, entityId, keys, null); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log timeseries delete", e); |
|
|
|
} |
|
|
|
resultFuture.set(tmp); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onFailure(Throwable t) { |
|
|
|
try { |
|
|
|
logTimeseriesDeleted(user, entityId, keys, t); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log timeseries delete", e); |
|
|
|
} |
|
|
|
resultFuture.setException(t); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
resultFuture.set(null); |
|
|
|
} |
|
|
|
return resultFuture; |
|
|
|
} |
|
|
|
|
|
|
|
private ListenableFuture<Void> deleteAttributesFromEntityView(EntityView entityView, String scope, List<String> keys, SecurityUser user) { |
|
|
|
EntityViewId entityId = entityView.getId(); |
|
|
|
SettableFuture<Void> resultFuture = SettableFuture.create(); |
|
|
|
if (keys != null && !keys.isEmpty()) { |
|
|
|
tsSubService.deleteAndNotify(entityView.getTenantId(), entityId, scope, keys, new FutureCallback<Void>() { |
|
|
|
@Override |
|
|
|
public void onSuccess(@Nullable Void tmp) { |
|
|
|
try { |
|
|
|
logAttributesDeleted(user, entityId, scope, keys, null); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log attribute delete", e); |
|
|
|
} |
|
|
|
resultFuture.set(tmp); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onFailure(Throwable t) { |
|
|
|
try { |
|
|
|
logAttributesDeleted(user, entityId, scope, keys, t); |
|
|
|
} catch (ThingsboardException e) { |
|
|
|
log.error("Failed to log attribute delete", e); |
|
|
|
} |
|
|
|
resultFuture.setException(t); |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
resultFuture.set(null); |
|
|
|
} |
|
|
|
return resultFuture; |
|
|
|
} |
|
|
|
|
|
|
|
private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) { |
|
|
|
EntityViewId entityId = entityView.getId(); |
|
|
|
List<String> keys = entityView.getKeys().getTimeseries(); |
|
|
|
long startTime = entityView.getStartTimeMs(); |
|
|
|
long endTime = entityView.getEndTimeMs(); |
|
|
|
ListenableFuture<List<TsKvEntry>> latestFuture; |
|
|
|
if (startTime == 0 && endTime == 0) { |
|
|
|
latestFuture = tsService.findLatest(user.getTenantId(), entityView.getEntityId(), keys); |
|
|
|
} else { |
|
|
|
long startTs = startTime; |
|
|
|
long endTs = endTime == 0 ? System.currentTimeMillis() : endTime; |
|
|
|
List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList()); |
|
|
|
latestFuture = tsService.findAll(user.getTenantId(), entityView.getEntityId(), queries); |
|
|
|
} |
|
|
|
return Futures.transform(latestFuture, latestValues -> { |
|
|
|
if (latestValues != null && !latestValues.isEmpty()) { |
|
|
|
tsSubService.saveLatestAndNotify(entityView.getTenantId(), entityId, latestValues, new FutureCallback<Void>() { |
|
|
|
@Override |
|
|
|
public void onSuccess(@Nullable Void tmp) { |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onFailure(Throwable t) { |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
return null; |
|
|
|
}, MoreExecutors.directExecutor()); |
|
|
|
} |
|
|
|
|
|
|
|
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()) { |
|
|
|
@ -174,10 +296,20 @@ public class EntityViewController extends BaseController { |
|
|
|
} |
|
|
|
|
|
|
|
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), |
|
|
|
logEntityAction(user, entityId, null, null, ActionType.ATTRIBUTES_UPDATED, toException(e), |
|
|
|
scope, attributes); |
|
|
|
} |
|
|
|
|
|
|
|
private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) throws ThingsboardException { |
|
|
|
logEntityAction(user, entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), |
|
|
|
scope, keys); |
|
|
|
} |
|
|
|
|
|
|
|
private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List<String> keys, Throwable e) throws ThingsboardException { |
|
|
|
logEntityAction(user, entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e), |
|
|
|
keys); |
|
|
|
} |
|
|
|
|
|
|
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
|
|
|
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE) |
|
|
|
@ResponseStatus(value = HttpStatus.OK) |
|
|
|
|