Enrichment Rule nodes config UI

This commit is contained in:
Igor Kulikov 2018-04-02 20:07:20 +03:00
parent 9c0f6e9925
commit c9717f4d1d
12 changed files with 95 additions and 28 deletions

View File

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.data;
import lombok.Data;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.EntityTypeFilter;
import java.util.List;
@Data
public class RelationsQuery {
private EntitySearchDirection direction;
private int maxLevel = 1;
private List<EntityTypeFilter> filters;
}

View File

@ -43,7 +43,9 @@ import static org.thingsboard.server.common.data.DataConstants.*;
nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " + nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
"with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " + "with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " +
"<code>metadata.cs.temperature</code> or <code>metadata.shared.limit</code> " + "<code>metadata.cs.temperature</code> or <code>metadata.shared.limit</code> " +
"If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.") "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeOriginatorAttributesConfig")
public class TbGetAttributesNode implements TbNode { public class TbGetAttributesNode implements TbNode {
private TbGetAttributesNodeConfiguration config; private TbGetAttributesNodeConfiguration config;

View File

@ -30,7 +30,9 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata", nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " + "To access those attributes in other nodes this template can be used " +
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
@Override @Override

View File

@ -16,18 +16,19 @@
package org.thingsboard.rule.engine.metadata; package org.thingsboard.rule.engine.metadata;
import lombok.Data; import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.data.RelationsQuery;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.EntityTypeFilter;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@Data @Data
public class TbGetRelatedAttrNodeConfiguration extends TbGetEntityAttrNodeConfiguration { public class TbGetRelatedAttrNodeConfiguration extends TbGetEntityAttrNodeConfiguration {
private String relationType; private RelationsQuery relationsQuery;
private EntitySearchDirection direction;
@Override @Override
public TbGetRelatedAttrNodeConfiguration defaultConfiguration() { public TbGetRelatedAttrNodeConfiguration defaultConfiguration() {
@ -36,8 +37,14 @@ public class TbGetRelatedAttrNodeConfiguration extends TbGetEntityAttrNodeConfig
attrMapping.putIfAbsent("temperature", "tempo"); attrMapping.putIfAbsent("temperature", "tempo");
configuration.setAttrMapping(attrMapping); configuration.setAttrMapping(attrMapping);
configuration.setTelemetry(true); configuration.setTelemetry(true);
configuration.setRelationType(EntityRelation.CONTAINS_TYPE);
configuration.setDirection(EntitySearchDirection.FROM); RelationsQuery relationsQuery = new RelationsQuery();
relationsQuery.setDirection(EntitySearchDirection.FROM);
relationsQuery.setMaxLevel(1);
EntityTypeFilter entityTypeFilter = new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList());
relationsQuery.setFilters(Collections.singletonList(entityTypeFilter));
configuration.setRelationsQuery(relationsQuery);
return configuration; return configuration;
} }
} }

View File

@ -32,7 +32,10 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
"If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " +
"If Attributes enrichment configured, server scope attributes are added into Message metadata. " + "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " + "To access those attributes in other nodes this template can be used " +
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
private TbGetRelatedAttrNodeConfiguration config; private TbGetRelatedAttrNodeConfiguration config;
@ -45,6 +48,6 @@ public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
@Override @Override
protected ListenableFuture<EntityId> findEntityAsync(TbContext ctx, EntityId originator) { protected ListenableFuture<EntityId> findEntityAsync(TbContext ctx, EntityId originator) {
return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, originator, config.getDirection(), config.getRelationType()); return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, originator, config.getRelationsQuery());
} }
} }

View File

@ -32,7 +32,9 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " + "To access those attributes in other nodes this template can be used " +
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbEnrichmentNodeTenantAttributesConfig")
public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
@Override @Override

View File

@ -68,7 +68,7 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode {
case TENANT_SOURCE: case TENANT_SOURCE:
return EntitiesTenantIdAsyncLoader.findEntityIdAsync(ctx, original); return EntitiesTenantIdAsyncLoader.findEntityIdAsync(ctx, original);
case RELATED_SOURCE: case RELATED_SOURCE:
return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, original, config.getDirection(), config.getRelationType()); return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, original, config.getRelationsQuery());
default: default:
return Futures.immediateFailedFuture(new IllegalStateException("Unexpected originator source " + config.getOriginatorSource())); return Futures.immediateFailedFuture(new IllegalStateException("Unexpected originator source " + config.getOriginatorSource()));
} }
@ -82,9 +82,9 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode {
} }
if (conf.getOriginatorSource().equals(RELATED_SOURCE)) { if (conf.getOriginatorSource().equals(RELATED_SOURCE)) {
if (conf.getDirection() == null || StringUtils.isBlank(conf.getRelationType())) { if (conf.getRelationsQuery() == null) {
log.error("Related source for TbChangeOriginatorNode should have direction and relationType. Actual [{}] [{}]", log.error("Related source for TbChangeOriginatorNode should have relations query. Actual [{}]",
conf.getDirection(), conf.getRelationType()); conf.getRelationsQuery());
throw new IllegalArgumentException("Wrong config for RElated Source in TbChangeOriginatorNode" + conf.getOriginatorSource()); throw new IllegalArgumentException("Wrong config for RElated Source in TbChangeOriginatorNode" + conf.getOriginatorSource());
} }
} }

View File

@ -17,22 +17,32 @@ package org.thingsboard.rule.engine.transform;
import lombok.Data; import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.data.RelationsQuery;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.EntityTypeFilter;
import java.util.Collections;
@Data @Data
public class TbChangeOriginatorNodeConfiguration extends TbTransformNodeConfiguration implements NodeConfiguration { public class TbChangeOriginatorNodeConfiguration extends TbTransformNodeConfiguration implements NodeConfiguration {
private String originatorSource; private String originatorSource;
private EntitySearchDirection direction;
private String relationType; private RelationsQuery relationsQuery;
@Override @Override
public TbChangeOriginatorNodeConfiguration defaultConfiguration() { public TbChangeOriginatorNodeConfiguration defaultConfiguration() {
TbChangeOriginatorNodeConfiguration configuration = new TbChangeOriginatorNodeConfiguration(); TbChangeOriginatorNodeConfiguration configuration = new TbChangeOriginatorNodeConfiguration();
configuration.setOriginatorSource(TbChangeOriginatorNode.CUSTOMER_SOURCE); configuration.setOriginatorSource(TbChangeOriginatorNode.CUSTOMER_SOURCE);
configuration.setDirection(EntitySearchDirection.FROM);
configuration.setRelationType(EntityRelation.CONTAINS_TYPE); RelationsQuery relationsQuery = new RelationsQuery();
relationsQuery.setDirection(EntitySearchDirection.FROM);
relationsQuery.setMaxLevel(1);
EntityTypeFilter entityTypeFilter = new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList());
relationsQuery.setFilters(Collections.singletonList(entityTypeFilter));
configuration.setRelationsQuery(relationsQuery);
configuration.setStartNewChain(false); configuration.setStartNewChain(false);
return configuration; return configuration;
} }

View File

@ -20,32 +20,41 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.data.RelationsQuery;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import java.util.List; import java.util.List;
import static org.thingsboard.server.common.data.relation.RelationTypeGroup.COMMON;
public class EntitiesRelatedEntityIdAsyncLoader { public class EntitiesRelatedEntityIdAsyncLoader {
public static ListenableFuture<EntityId> findEntityAsync(TbContext ctx, EntityId originator, public static ListenableFuture<EntityId> findEntityAsync(TbContext ctx, EntityId originator,
EntitySearchDirection direction, String relationType) { RelationsQuery relationsQuery) {
RelationService relationService = ctx.getRelationService(); RelationService relationService = ctx.getRelationService();
if (direction == EntitySearchDirection.FROM) { EntityRelationsQuery query = buildQuery(originator, relationsQuery);
ListenableFuture<List<EntityRelation>> asyncRelation = relationService.findByFromAndTypeAsync(originator, relationType, COMMON); ListenableFuture<List<EntityRelation>> asyncRelation = relationService.findByQuery(query);
if (relationsQuery.getDirection() == EntitySearchDirection.FROM) {
return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>) return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>)
r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getTo()) r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getTo())
: Futures.immediateFailedFuture(new IllegalStateException("Relation not found"))); : Futures.immediateFailedFuture(new IllegalStateException("Relation not found")));
} else if (direction == EntitySearchDirection.TO) { } else if (relationsQuery.getDirection() == EntitySearchDirection.TO) {
ListenableFuture<List<EntityRelation>> asyncRelation = relationService.findByToAndTypeAsync(originator, relationType, COMMON);
return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>) return Futures.transform(asyncRelation, (AsyncFunction<? super List<EntityRelation>, EntityId>)
r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getFrom()) r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getFrom())
: Futures.immediateFailedFuture(new IllegalStateException("Relation not found"))); : Futures.immediateFailedFuture(new IllegalStateException("Relation not found")));
} }
return Futures.immediateFailedFuture(new IllegalStateException("Unknown direction")); return Futures.immediateFailedFuture(new IllegalStateException("Unknown direction"));
} }
private static EntityRelationsQuery buildQuery(EntityId originator, RelationsQuery relationsQuery) {
EntityRelationsQuery query = new EntityRelationsQuery();
RelationsSearchParameters parameters = new RelationsSearchParameters(originator,
relationsQuery.getDirection(), relationsQuery.getMaxLevel());
query.setParameters(parameters);
query.setFilters(relationsQuery.getFilters());
return query;
}
} }

View File

@ -1,2 +1,2 @@
.tb-message-type-autocomplete .tb-not-found{display:block;line-height:1.5;height:48px}.tb-message-type-autocomplete .tb-not-found .tb-no-entries{line-height:48px}.tb-message-type-autocomplete li{height:auto!important;white-space:normal!important} .tb-message-type-autocomplete .tb-not-found{display:block;line-height:1.5;height:48px}.tb-message-type-autocomplete .tb-not-found .tb-no-entries{line-height:48px}.tb-message-type-autocomplete li{height:auto!important;white-space:normal!important}.tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}.tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:rgba(0,0,0,.54);font-size:12px;font-weight:700;white-space:nowrap}.tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:20px;max-height:300px;overflow:auto}.tb-kv-map-config .body .row{padding-top:5px;max-height:40px}.tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}.tb-kv-map-config .body md-input-container.cell{margin:0;max-height:40px}.tb-kv-map-config .body .md-button{margin:0}
/*# sourceMappingURL=rulenode-core-config.css.map*/ /*# sourceMappingURL=rulenode-core-config.css.map*/

View File

@ -46,6 +46,7 @@ export default function RelationFilters($compile, $templateCache) {
ngModelCtrl.$render = function () { ngModelCtrl.$render = function () {
if (ngModelCtrl.$viewValue) { if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue; var value = ngModelCtrl.$viewValue;
scope.relationFilters.length = 0;
value.forEach(function (filter) { value.forEach(function (filter) {
scope.relationFilters.push(filter); scope.relationFilters.push(filter);
}); });