thingsboard-aplcache

Details

diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index d33734e..e3682de 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -48,6 +48,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
 import org.thingsboard.server.dao.audit.AuditLogService;
 import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
 import org.thingsboard.server.dao.event.EventService;
 import org.thingsboard.server.dao.relation.RelationService;
 import org.thingsboard.server.dao.rule.RuleChainService;
@@ -161,6 +162,10 @@ public class ActorSystemContext {
 
     @Autowired
     @Getter
+    private EntityViewService entityViewService;
+
+    @Autowired
+    @Getter
     private TelemetrySubscriptionService tsSubService;
 
     @Autowired
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index 7279347..acfc269 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -41,6 +41,7 @@ import org.thingsboard.server.dao.asset.AssetService;
 import org.thingsboard.server.dao.attributes.AttributesService;
 import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
 import org.thingsboard.server.dao.relation.RelationService;
 import org.thingsboard.server.dao.rule.RuleChainService;
 import org.thingsboard.server.dao.tenant.TenantService;
@@ -213,6 +214,11 @@ class DefaultTbContext implements TbContext {
     }
 
     @Override
+    public EntityViewService getEntityViewService() {
+        return mainCtx.getEntityViewService();
+    }
+
+    @Override
     public MailService getMailService() {
         if (mainCtx.isAllowSystemMailService()) {
             return mainCtx.getMailService();
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
index 5716460..5de34e4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entityview;
 
 import com.datastax.driver.core.Statement;
 import com.datastax.driver.core.querybuilder.Select;
+import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.EntitySubtype;
@@ -105,4 +106,10 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
     public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(UUID tenantId, UUID customerId, UUID entityId, TextPageLink pageLink) {
         return null;
     }
+
+    @Override
+    public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
+        // TODO: implement this
+        return null;
+    }
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
index 56a688a..be643ef 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
@@ -15,6 +15,8 @@
  */
 package org.thingsboard.server.dao.entityview;
 
+import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.EntityView;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
@@ -91,4 +93,6 @@ public interface EntityViewDao extends Dao<EntityView> {
                                                                        UUID customerId,
                                                                        UUID entityId,
                                                                        TextPageLink pageLink);
+
+    ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
index a60376e..b9c238a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
 import org.thingsboard.server.common.data.id.*;
@@ -65,4 +66,6 @@ public interface EntityViewService {
     ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
 
     ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query);
+
+    ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
index d86eabb..598897a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
@@ -25,11 +25,13 @@ import org.springframework.cache.CacheManager;
 import org.springframework.stereotype.Service;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.Device;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.EntityView;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
 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;
@@ -52,8 +54,10 @@ import java.util.Collection;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
 import static org.thingsboard.server.dao.service.Validator.validateId;
+import static org.thingsboard.server.dao.service.Validator.validateIds;
 import static org.thingsboard.server.dao.service.Validator.validatePageLink;
 import static org.thingsboard.server.dao.service.Validator.validateString;
 
@@ -277,6 +281,14 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
         return entityViews;
     }
 
+    @Override
+    public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) {
+        log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId);
+        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
+        validateId(entityId.getId(), "Incorrect entityId" + entityId);
+        return entityViewDao.findEntityViewsByTenantIdAndEntityIdAsync(tenantId.getId(), entityId.getId());
+    }
+
     private DataValidator<EntityView> entityViewValidator =
             new DataValidator<EntityView>() {
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java
index 27bd839..f1ee3fd 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java
@@ -19,6 +19,7 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.query.Param;
+import org.thingsboard.server.common.data.EntityView;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.dao.model.sql.EntityViewEntity;
 import org.thingsboard.server.dao.util.SqlDao;
@@ -78,4 +79,6 @@ public interface EntityViewRepository extends CrudRepository<EntityViewEntity, S
                                                                  List<String> entityViewsIds);
 
     List<EntityViewEntity> findAllByTenantIdAndIdIn(String tenantId, List<String> entityViewsIds);
+
+    List<EntityViewEntity> findAllByTenantIdAndEntityId(String tenantId, String entityId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
index 2f3edbe..4ff21ab 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
@@ -23,6 +23,7 @@ import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.UUIDConverter;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -40,6 +41,7 @@ import java.util.Optional;
 import java.util.UUID;
 
 import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
+import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs;
 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR;
 
 /**
@@ -121,4 +123,10 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity,
                         new PageRequest(0, pageLink.getLimit())
                 ));
     }
+
+    @Override
+    public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
+        return service.submit(() -> DaoUtil.convertDataList(
+                entityViewRepository.findAllByTenantIdAndEntityId(UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(entityId))));
+    }
 }
diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
index e7ef0dd..f9d3c64 100644
--- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
+++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java
@@ -26,6 +26,7 @@ import org.thingsboard.server.dao.asset.AssetService;
 import org.thingsboard.server.dao.attributes.AttributesService;
 import org.thingsboard.server.dao.customer.CustomerService;
 import org.thingsboard.server.dao.device.DeviceService;
+import org.thingsboard.server.dao.entityview.EntityViewService;
 import org.thingsboard.server.dao.relation.RelationService;
 import org.thingsboard.server.dao.rule.RuleChainService;
 import org.thingsboard.server.dao.tenant.TenantService;
@@ -83,6 +84,8 @@ public interface TbContext {
 
     RelationService getRelationService();
 
+    EntityViewService getEntityViewService();
+
     ListeningExecutor getJsExecutor();
 
     ListeningExecutor getMailExecutor();
diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
new file mode 100644
index 0000000..73999af
--- /dev/null
+++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
@@ -0,0 +1,122 @@
+package org.thingsboard.rule.engine.action;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Function;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.gson.JsonParser;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
+import org.thingsboard.rule.engine.api.RuleNode;
+import org.thingsboard.rule.engine.api.TbContext;
+import org.thingsboard.rule.engine.api.TbNode;
+import org.thingsboard.rule.engine.api.TbNodeConfiguration;
+import org.thingsboard.rule.engine.api.TbNodeException;
+import org.thingsboard.rule.engine.api.TbRelationTypes;
+import org.thingsboard.rule.engine.api.util.TbNodeUtils;
+import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.kv.AttributeKvEntry;
+import org.thingsboard.server.common.data.kv.TsKvEntry;
+import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.msg.TbMsg;
+import org.thingsboard.server.common.msg.session.SessionMsgType;
+import org.thingsboard.server.common.transport.adaptor.JsonConverter;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
+
+@Slf4j
+@RuleNode(
+        type = ComponentType.ACTION,
+        name = "copy attributes",
+        configClazz = EmptyNodeConfiguration.class,
+        nodeDescription = "Copy attributes from asset/device to entity view",
+        nodeDetails = "Copy attributes from asset/device to related entity view according to entity view configuration. \n " +
+                "Copy will be done only for attributes that are between start and end dates and according to attribute keys configuration",
+        uiResources = {"static/rulenode/rulenode-core-config.js"},
+        configDirective = "tbNodeEmptyConfig",
+        icon = "content_copy"
+)
+public class TbCopyAttributesToEntityViewNode implements TbNode {
+
+    EmptyNodeConfiguration config;
+
+    @Override
+    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
+        this.config = TbNodeUtils.convert(configuration, EmptyNodeConfiguration.class);
+    }
+
+    @Override
+    public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
+        if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ||
+                msg.getType().equals(DataConstants.ATTRIBUTES_DELETED) ||
+                msg.getType().equals(DataConstants.ATTRIBUTES_UPDATED)) {
+            long now = System.currentTimeMillis();
+            String scope;
+            if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) {
+                scope = DataConstants.CLIENT_SCOPE;
+            } else {
+                scope = msg.getMetaData().getValue("scope");
+            }
+            ListenableFuture<List<EntityView>> entityViewsFuture =
+                    ctx.getEntityViewService().findEntityViewsByTenantIdAndEntityIdAsync(ctx.getTenantId(), msg.getOriginator());
+            withCallback(entityViewsFuture,
+                    entityViews -> {
+                        List<ListenableFuture<List<Void>>> saveFutures = new ArrayList<>();
+                        for (EntityView entityView : entityViews) {
+                            if ((entityView.getEndTimeMs() != 0  && entityView.getEndTimeMs() > now && entityView.getStartTimeMs() < now) ||
+                                    (entityView.getEndTimeMs() == 0 && entityView.getStartTimeMs() < now)) {
+                                Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())).getAttributes();
+                                List<AttributeKvEntry> filteredAttributes =
+                                        attributes.stream()
+                                                .filter(attr -> {
+                                                    switch (scope) {
+                                                        case DataConstants.CLIENT_SCOPE:
+                                                            if (entityView.getKeys().getAttributes().getCs().isEmpty()) {
+                                                                return true;
+                                                            }
+                                                            return entityView.getKeys().getAttributes().getCs().contains(attr.getKey());
+                                                        case DataConstants.SERVER_SCOPE:
+                                                            if (entityView.getKeys().getAttributes().getSs().isEmpty()) {
+                                                                return true;
+                                                            }
+                                                            return entityView.getKeys().getAttributes().getSs().contains(attr.getKey());
+                                                        case  DataConstants.SHARED_SCOPE:
+                                                            if (entityView.getKeys().getAttributes().getSh().isEmpty()) {
+                                                                return true;
+                                                            }
+                                                            return entityView.getKeys().getAttributes().getSh().contains(attr.getKey());
+                                                    }
+                                                    return false;
+                                                })
+                                                .collect(Collectors.toList());
+                                saveFutures.add(ctx.getAttributesService().save(entityView.getId(), scope, new ArrayList<>(filteredAttributes)));
+                            }
+                        }
+                        Futures.transform(Futures.allAsList(saveFutures), new Function<List<List<Void>>, Object>() {
+                            @Nullable
+                            @Override
+                            public Object apply(@Nullable List<List<Void>> lists) {
+                                ctx.tellNext(msg, TbRelationTypes.SUCCESS);
+                                return null;
+                            }
+                        });
+                    },
+                    t -> ctx.tellFailure(msg, t));
+        } else {
+            ctx.tellNext(msg, TbRelationTypes.FAILURE);
+        }
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}