@ -15,7 +15,10 @@
* /
* /
package org.thingsboard.server.controller ;
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 com.google.common.util.concurrent.ListenableFuture ;
import lombok.extern.slf4j.Slf4j ;
import org.springframework.http.HttpStatus ;
import org.springframework.http.HttpStatus ;
import org.springframework.security.access.prepost.PreAuthorize ;
import org.springframework.security.access.prepost.PreAuthorize ;
import org.springframework.web.bind.annotation.PathVariable ;
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.ResponseBody ;
import org.springframework.web.bind.annotation.ResponseStatus ;
import org.springframework.web.bind.annotation.ResponseStatus ;
import org.springframework.web.bind.annotation.RestController ;
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.Customer ;
import org.thingsboard.server.common.data.DataConstants ;
import org.thingsboard.server.common.data.EntitySubtype ;
import org.thingsboard.server.common.data.EntitySubtype ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.EntityView ;
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.entityview.EntityViewSearchQuery ;
import org.thingsboard.server.common.data.exception.ThingsboardException ;
import org.thingsboard.server.common.data.exception.ThingsboardException ;
import org.thingsboard.server.common.data.id.CustomerId ;
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.EntityViewId ;
import org.thingsboard.server.common.data.id.TenantId ;
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.TextPageData ;
import org.thingsboard.server.common.data.page.TextPageLink ;
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.exception.IncorrectParameterException ;
import org.thingsboard.server.dao.model.ModelConstants ;
import org.thingsboard.server.dao.model.ModelConstants ;
import org.thingsboard.server.service.security.model.SecurityUser ;
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.List ;
import java.util.concurrent.ExecutionException ;
import java.util.stream.Collectors ;
import java.util.stream.Collectors ;
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID ;
import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID ;
@ -52,6 +66,7 @@ import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
* /
* /
@RestController
@RestController
@RequestMapping ( "/api" )
@RequestMapping ( "/api" )
@Slf4j
public class EntityViewController extends BaseController {
public class EntityViewController extends BaseController {
public static final String ENTITY_VIEW_ID = "entityViewId" ;
public static final String ENTITY_VIEW_ID = "entityViewId" ;
@ -75,6 +90,20 @@ public class EntityViewController extends BaseController {
try {
try {
entityView . setTenantId ( getCurrentUser ( ) . getTenantId ( ) ) ;
entityView . setTenantId ( getCurrentUser ( ) . getTenantId ( ) ) ;
EntityView savedEntityView = checkNotNull ( entityViewService . saveEntityView ( entityView ) ) ;
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 ,
logEntityAction ( savedEntityView . getId ( ) , savedEntityView , null ,
entityView . getId ( ) = = null ? ActionType . ADDED : ActionType . UPDATED , null ) ;
entityView . getId ( ) = = null ? ActionType . ADDED : ActionType . UPDATED , null ) ;
return savedEntityView ;
return savedEntityView ;
@ -85,6 +114,56 @@ 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 ) ;
}
}
@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')" )
@PreAuthorize ( "hasAuthority('TENANT_ADMIN')" )
@RequestMapping ( value = "/entityView/{entityViewId}" , method = RequestMethod . DELETE )
@RequestMapping ( value = "/entityView/{entityViewId}" , method = RequestMethod . DELETE )
@ResponseStatus ( value = HttpStatus . OK )
@ResponseStatus ( value = HttpStatus . OK )