Merge pull request #12407 from vvlladd28/feature/rule-node/component
Migrated rule node config components from another repository
This commit is contained in:
commit
d3a07a42e9
@ -40,7 +40,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDescription = "Assign message originator entity to customer",
|
||||
nodeDetails = "Finds target customer by title and assign message originator entity to this customer. " +
|
||||
"Rule node will create a new customer if it doesn't exist, and 'Create new customer if it doesn't exist' enabled.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeAssignToCustomerConfig",
|
||||
icon = "add_circle",
|
||||
version = 1
|
||||
|
||||
@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
"If alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. " +
|
||||
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
|
||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeClearAlarmConfig",
|
||||
icon = "notifications_off"
|
||||
)
|
||||
|
||||
@ -63,7 +63,6 @@ import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCES
|
||||
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. \n" +
|
||||
"Changes message originator to related entity view and produces new messages according to count of updated entity views",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig",
|
||||
icon = "content_copy"
|
||||
)
|
||||
|
||||
@ -51,7 +51,6 @@ import java.util.List;
|
||||
"If alarm was not created, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains one of those properties 'isNewAlarm/isExistingAlarm'. " +
|
||||
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
|
||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeCreateAlarmConfig",
|
||||
icon = "notifications_active"
|
||||
)
|
||||
|
||||
@ -61,7 +61,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"Useful in GPS tracking use cases where relation acts as a temporary indicator of a tracker presence in specific geofence.</li>" +
|
||||
"<li><strong>Change originator to target entity</strong> - useful when you need to process submitted message as a message from target entity.</li></ul>" +
|
||||
"Output connections: <code>Success</code> - if the relation already exists or successfully created, otherwise <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeCreateRelationConfig",
|
||||
icon = "add_circle",
|
||||
version = 1
|
||||
|
||||
@ -53,7 +53,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"<li><strong>User</strong> - use a user with the specified email as the target entity to delete relation with.</li>" +
|
||||
"<li><strong>Edge</strong> - use an edge with the specified name as the target entity to delete relation with.</li></ul>" +
|
||||
"Output connections: <code>Success</code> - If the relation(s) successfully deleted, otherwise <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeDeleteRelationConfig",
|
||||
icon = "remove_circle",
|
||||
version = 1
|
||||
|
||||
@ -58,7 +58,6 @@ import java.util.Set;
|
||||
"This node is particularly useful when device isn't using transports to receive data, such as when fetching data from external API or computing new data within the rule chain.",
|
||||
configClazz = TbDeviceStateNodeConfiguration.class,
|
||||
relationTypes = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE, "Rate limited"},
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeDeviceStateConfig"
|
||||
)
|
||||
public class TbDeviceStateNode implements TbNode {
|
||||
|
||||
@ -43,7 +43,6 @@ import java.util.Objects;
|
||||
nodeDetails = "Transform incoming Message with configured JS function to String and log final value into Thingsboard log file. " +
|
||||
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
|
||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeLogConfig",
|
||||
icon = "menu"
|
||||
)
|
||||
|
||||
@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
nodeDescription = "Count incoming messages",
|
||||
nodeDetails = "Count incoming messages for specified interval and produces POST_TELEMETRY_REQUEST msg with messages count",
|
||||
icon = "functions",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeMsgCountConfig"
|
||||
)
|
||||
public class TbMsgCountNode implements TbNode {
|
||||
|
||||
@ -69,7 +69,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"<b>Note:</b>If the mapping key is <b>$entity_id</b>, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.<br><br>" +
|
||||
"If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via <b>failure</b> chain," +
|
||||
" otherwise, the message will be routed via <b>success</b> chain.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeCustomTableConfig",
|
||||
icon = "file_upload",
|
||||
ruleChainTypes = RuleChainType.CORE)
|
||||
|
||||
@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
"If the incoming message originator is a dashboard, will try to search for the customer by title specified in the configuration. " +
|
||||
"If customer doesn't exist, the exception will be thrown. Otherwise will unassign the dashboard from retrieved customer.<br><br>" +
|
||||
"Other entities can be assigned only to one customer, so specified customer title in the configuration will be ignored if the originator isn't a dashboard.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeUnAssignToCustomerConfig",
|
||||
icon = "remove_circle",
|
||||
version = 1
|
||||
|
||||
@ -53,7 +53,6 @@ import static org.thingsboard.server.dao.service.ConstraintValidator.validateFie
|
||||
"It sends messages using a RequestResponse invocation type. " +
|
||||
"The node uses a pre-configured client and specified function to run.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeLambdaConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
|
||||
)
|
||||
|
||||
@ -46,7 +46,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
nodeDetails = "Will publish message payload to the AWS SNS topic. Outbound message will contain response fields " +
|
||||
"(<code>messageId</code>, <code>requestId</code>) in the Message Metadata from the AWS SNS. " +
|
||||
"For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeSnsConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
|
||||
)
|
||||
|
||||
@ -52,7 +52,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"response fields (<code>messageId</code>, <code>requestId</code>, <code>messageBodyMd5</code>, <code>messageAttributesMd5</code>" +
|
||||
", <code>sequenceNumber</code>) in the Message Metadata from the AWS SQS." +
|
||||
" For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeSqsConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
|
||||
)
|
||||
|
||||
@ -62,7 +62,6 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
|
||||
nodeDescription = "Periodically generates messages",
|
||||
nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.",
|
||||
inEnabled = false,
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeGeneratorConfig",
|
||||
icon = "repeat"
|
||||
)
|
||||
|
||||
@ -59,8 +59,7 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
|
||||
"<li><strong>ALL</strong> - return all messages as a single JSON array message. " +
|
||||
"Where each element represents object with <strong><i>msg</i></strong> and <strong><i>metadata</i></strong> inner properties.</li></ul>",
|
||||
icon = "content_copy",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeMsgDeduplicationConfig"
|
||||
configDirective = "tbTransformationNodeDeduplicationConfig"
|
||||
)
|
||||
@Slf4j
|
||||
public class TbMsgDeduplicationNode implements TbNode {
|
||||
|
||||
@ -45,7 +45,6 @@ import java.util.concurrent.TimeUnit;
|
||||
"Deprecated because the acknowledged message still stays in memory (to be delayed) and this " +
|
||||
"does not guarantee that message will be processed even if the \"retry failures and timeouts\" processing strategy will be chosen.",
|
||||
icon = "pause",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeMsgDelayConfig"
|
||||
)
|
||||
public class TbMsgDelayNode implements TbNode {
|
||||
|
||||
@ -46,7 +46,6 @@ import java.util.UUID;
|
||||
"<br><code>ALARM</code><br><br>" +
|
||||
"Message will be routed via <b>Failure</b> route if node was not able to save cloud event to database or unsupported originator type/message type arrived. " +
|
||||
"In case successful storage cloud event to database message will be routed via <b>Success</b> route.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodePushToCloudConfig",
|
||||
icon = "cloud_upload",
|
||||
ruleChainTypes = RuleChainType.EDGE
|
||||
|
||||
@ -61,7 +61,6 @@ import static org.thingsboard.server.dao.edge.BaseRelatedEdgesService.RELATED_ED
|
||||
"<br><code>ALARM</code><br><br>" +
|
||||
"Message will be routed via <b>Failure</b> route if node was not able to save edge event to database or unsupported message type arrived. " +
|
||||
"In case successful storage edge event to database message will be routed via <b>Success</b> route.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodePushToEdgeConfig",
|
||||
icon = "cloud_download",
|
||||
ruleChainTypes = RuleChainType.CORE
|
||||
|
||||
@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
nodeDescription = "Route incoming messages based on the name of the asset profile",
|
||||
nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive.<br><br>" +
|
||||
"Output connections: <i>Asset profile name</i> or <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig")
|
||||
public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode {
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ import java.util.Objects;
|
||||
nodeDescription = "Checks alarm status.",
|
||||
nodeDetails = "Checks the alarm status to match one of the specified statuses.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeCheckAlarmStatusConfig")
|
||||
public class TbCheckAlarmStatusNode implements TbNode {
|
||||
|
||||
|
||||
@ -40,7 +40,6 @@ import java.util.Map;
|
||||
nodeDetails = "By default, the rule node checks that all specified fields are present. " +
|
||||
"Uncheck the 'Check that all selected fields are present' if the presence of at least one field is sufficient.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeCheckMessageConfig")
|
||||
public class TbCheckMessageNode implements TbNode {
|
||||
|
||||
|
||||
@ -56,7 +56,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"Otherwise, the rule node checks the presence of a relation to any entity. " +
|
||||
"In both cases, relation lookup is based on configured direction and type.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeCheckRelationConfig")
|
||||
public class TbCheckRelationNode implements TbNode {
|
||||
|
||||
|
||||
@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
nodeDescription = "Route incoming messages based on the name of the device profile",
|
||||
nodeDetails = "Route incoming messages based on the name of the device profile. The device profile name is case-sensitive<br><br>" +
|
||||
"Output connections: <i>Device profile name</i> or <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig")
|
||||
public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode {
|
||||
|
||||
|
||||
@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" +
|
||||
"Message type can be accessed via <code>msgType</code> property.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeScriptConfig"
|
||||
)
|
||||
public class TbJsFilterNode implements TbNode {
|
||||
|
||||
@ -46,7 +46,6 @@ import java.util.Set;
|
||||
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" +
|
||||
"Message type can be accessed via <code>msgType</code> property.<br><br>" +
|
||||
"Output connections: <i>Custom connection(s) defined by switch node</i> or <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeSwitchConfig")
|
||||
public class TbJsSwitchNode implements TbNode {
|
||||
|
||||
|
||||
@ -38,7 +38,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDescription = "Filter incoming messages by Message Type",
|
||||
nodeDetails = "If incoming message type is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeMessageTypeConfig")
|
||||
public class TbMsgTypeFilterNode implements TbNode {
|
||||
|
||||
|
||||
@ -36,7 +36,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b>" +
|
||||
" etc. via corresponding chain, otherwise <b>Other</b> chain is used.<br><br>" +
|
||||
"Output connections: <i>Message type connection</i>, <code>Other</code> - if message type is custom or <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig")
|
||||
public class TbMsgTypeSwitchNode implements TbNode {
|
||||
|
||||
|
||||
@ -36,7 +36,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDescription = "Filter incoming messages by the type of message originator entity",
|
||||
nodeDetails = "Checks that the entity type of the incoming message originator matches one of the values specified in the filter.<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeOriginatorTypeConfig")
|
||||
public class TbOriginatorTypeFilterNode implements TbNode {
|
||||
|
||||
|
||||
@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
nodeDescription = "Route incoming messages by Message Originator Type",
|
||||
nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).<br><br>" +
|
||||
"Output connections: <i>Message originator type</i> or <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig")
|
||||
public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode {
|
||||
|
||||
|
||||
@ -33,7 +33,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
configClazz = EmptyNodeConfiguration.class,
|
||||
nodeDescription = "Acknowledges the incoming message",
|
||||
nodeDetails = "After acknowledgement, the message is pushed to related rule nodes. Useful if you don't care what happens to this message next.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig"
|
||||
)
|
||||
public class TbAckNode implements TbNode {
|
||||
|
||||
@ -40,7 +40,6 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
|
||||
hasQueueName = true,
|
||||
nodeDescription = "transfers the message to another queue",
|
||||
nodeDetails = "After successful transfer incoming message is automatically acknowledged. Queue name is configurable.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig"
|
||||
)
|
||||
public class TbCheckpointNode implements TbNode {
|
||||
|
||||
@ -46,7 +46,6 @@ import java.util.UUID;
|
||||
"then target rule chain might be resolved dynamically based on incoming message originator. " +
|
||||
"In this case rule chain specified in the configuration will be used as fallback rule chain.<br><br>" +
|
||||
"Output connections: <i>Any connection(s) produced by output node(s) in the target rule chain.</i>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFlowNodeRuleChainInputConfig",
|
||||
relationTypes = {},
|
||||
ruleChainNode = true,
|
||||
|
||||
@ -34,7 +34,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDetails = "Produces output of the rule chain processing. " +
|
||||
"The output is forwarded to the caller rule chain, as an output of the corresponding \"input\" rule node. " +
|
||||
"The output rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. ",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFlowNodeRuleChainOutputConfig",
|
||||
outEnabled = false
|
||||
)
|
||||
|
||||
@ -52,7 +52,6 @@ import java.util.concurrent.TimeUnit;
|
||||
nodeDetails = "Will publish message payload to the Google Cloud Platform PubSub topic. Outbound message will contain response fields " +
|
||||
"(<code>messageId</code> in the Message Metadata from the GCP PubSub. " +
|
||||
"<b>messageId</b> field can be accessed with <code>metadata.messageId</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodePubSubConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo="
|
||||
)
|
||||
|
||||
@ -66,7 +66,6 @@ import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE;
|
||||
"If the presence monitoring strategy <b>\"On each message\"</b> is selected, sends messages via rule node connection type <code>Inside</code> or <code>Outside</code> every time the geofencing condition is satisfied. " +
|
||||
"<br><br>" +
|
||||
"Output connections: <code>Entered</code>, <code>Left</code>, <code>Inside</code>, <code>Outside</code>, <code>Success</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeGpsGeofencingConfig"
|
||||
)
|
||||
public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {
|
||||
|
||||
@ -60,7 +60,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
"</br></br>" +
|
||||
"Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;<br><br>" +
|
||||
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbFilterNodeGpsGeofencingConfig")
|
||||
public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> {
|
||||
|
||||
|
||||
@ -52,7 +52,6 @@ import java.util.Properties;
|
||||
nodeDetails = "Will send record via Kafka producer to Kafka server. " +
|
||||
"Outbound message will contain response fields (<code>offset</code>, <code>partition</code>, <code>topic</code>)" +
|
||||
" from the Kafka in the Message Metadata. For example <b>partition</b> field can be accessed with <code>metadata.partition</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeKafkaConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg=="
|
||||
)
|
||||
|
||||
@ -42,7 +42,6 @@ import java.util.Map;
|
||||
nodeDescription = "Transforms message to email message",
|
||||
nodeDetails = "Transforms message to email message. If transformation completed successfully output message type will be set to <code>SEND_EMAIL</code>.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeToEmailConfig",
|
||||
icon = "email"
|
||||
)
|
||||
|
||||
@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
nodeDetails = "Expects messages with <b>SEND_EMAIL</b> type. Node works only with messages that " +
|
||||
" where created using <code>to Email</code> transformation Node, please connect this Node " +
|
||||
"with <code>to Email</code> Node using <code>Successful</code> chain.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeSendEmailConfig",
|
||||
icon = "send"
|
||||
)
|
||||
|
||||
@ -76,7 +76,6 @@ import static org.thingsboard.rule.engine.math.TbMathArgumentType.CONSTANT;
|
||||
"<br/><br/>" +
|
||||
"The execution is synchronized in scope of message originator (e.g. device) and server node. " +
|
||||
"If you have rule nodes in different rule chains, they will process messages from the same originator synchronously in the scope of the server node.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeMathFunctionConfig",
|
||||
icon = "calculate"
|
||||
|
||||
|
||||
@ -53,7 +53,6 @@ import java.util.Map;
|
||||
"and current value for this key from the incoming message",
|
||||
nodeDetails = "Useful for metering use cases, when you need to calculate consumption based on pulse counter reading.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Other</code> or <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeCalculateDeltaConfig")
|
||||
public class CalculateDeltaNode implements TbNode {
|
||||
|
||||
|
||||
@ -45,7 +45,6 @@ import java.util.concurrent.ExecutionException;
|
||||
"Useful when you need to fetch device credentials and use them for further message processing. " +
|
||||
"For example, use device credentials to interact with external systems.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeFetchDeviceCredentialsConfig")
|
||||
public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo<TbFetchDeviceCredentialsNodeConfiguration> {
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
"that are not included in the incoming message to use them for further message processing. " +
|
||||
"For example to filter messages based on the threshold value stored in the attributes.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeOriginatorAttributesConfig")
|
||||
public class TbGetAttributesNode extends TbAbstractGetAttributesNode<TbGetAttributesNodeConfiguration, EntityId> {
|
||||
|
||||
|
||||
@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.util.TbPair;
|
||||
"that is stored as customer attributes or telemetry data and used for dynamic message filtering, transformation, " +
|
||||
"or actions such as alarm creation if the threshold is exceeded.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
|
||||
public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode<CustomerId> {
|
||||
|
||||
|
||||
@ -50,7 +50,6 @@ import java.util.NoSuchElementException;
|
||||
nodeDetails = "Useful in multi-customer solutions where we need dynamically use customer contact information " +
|
||||
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeEntityDetailsConfig")
|
||||
public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetCustomerDetailsNodeConfiguration, CustomerId> {
|
||||
|
||||
|
||||
@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
"Useful when you need to retrieve attributes and/or latest telemetry values from device that has a relation " +
|
||||
"to the message originator and use them for further message processing.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeDeviceAttributesConfig")
|
||||
public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode<TbGetDeviceAttrNodeConfiguration, DeviceId> {
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
|
||||
nodeDetails = "Fetches fields values specified in the mapping. If specified field is not part of originator fields it will be ignored. " +
|
||||
"Useful when you need to retrieve originator fields and use them for further message processing.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeOriginatorFieldsConfig")
|
||||
public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode<EntityId, TbGetOriginatorFieldsConfiguration> {
|
||||
|
||||
|
||||
@ -42,7 +42,6 @@ import java.util.Arrays;
|
||||
"If multiple related entities are found, only first entity is used for message enrichment, other entities are discarded. " +
|
||||
"Useful when you need to retrieve data from an entity that has a relation to the message originator and use them for further message processing.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
|
||||
public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode<EntityId> {
|
||||
|
||||
|
||||
@ -58,7 +58,6 @@ import java.util.stream.Collectors;
|
||||
"instead of fetching just the latest telemetry or if you need to get the closest telemetry to the fetch interval start or end. " +
|
||||
"Also, this node can be used for telemetry aggregation within configured fetch interval.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase")
|
||||
public class TbGetTelemetryNode implements TbNode {
|
||||
|
||||
|
||||
@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.util.TbPair;
|
||||
nodeDetails = "Useful when you need to retrieve some common configuration or threshold set " +
|
||||
"that is stored as tenant attributes or telemetry data and use it for further message processing.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeTenantAttributesConfig")
|
||||
public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode<TenantId> {
|
||||
|
||||
|
||||
@ -39,7 +39,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDetails = "Useful when we need to retrieve contact information from your tenant " +
|
||||
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbEnrichmentNodeEntityDetailsConfig")
|
||||
public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetTenantDetailsNodeConfiguration, TenantId> {
|
||||
|
||||
|
||||
@ -57,7 +57,6 @@ import java.util.concurrent.TimeoutException;
|
||||
clusteringMode = ComponentClusteringMode.USER_PREFERENCE,
|
||||
nodeDescription = "Publish messages to the MQTT broker",
|
||||
nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeMqttConfig",
|
||||
icon = "call_split"
|
||||
)
|
||||
|
||||
@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
clusteringMode = ComponentClusteringMode.SINGLETON,
|
||||
nodeDescription = "Publish messages to the Azure IoT Hub",
|
||||
nodeDetails = "Will publish message payload to the Azure IoT Hub with QoS <b>AT_LEAST_ONCE</b>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeAzureIotHubConfig"
|
||||
)
|
||||
public class TbAzureIotHubNode extends TbMqttNode {
|
||||
|
||||
@ -41,7 +41,6 @@ import java.util.concurrent.ExecutionException;
|
||||
configClazz = TbNotificationNodeConfiguration.class,
|
||||
nodeDescription = "Sends notification to targets using the template",
|
||||
nodeDetails = "Will send notification to the specified targets using the template",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeNotificationConfig",
|
||||
icon = "notifications"
|
||||
)
|
||||
|
||||
@ -33,7 +33,6 @@ import java.util.concurrent.ExecutionException;
|
||||
configClazz = TbSlackNodeConfiguration.class,
|
||||
nodeDescription = "Send message via Slack",
|
||||
nodeDetails = "Sends message to a Slack channel or user",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeSlackConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsMTVBMiwyIDAgMCwxIDQsMTdBMiwyIDAgMCwxIDIsMTVBMiwyIDAgMCwxIDQsMTNINlYxNU03LDE1QTIsMiAwIDAsMSA5LDEzQTIsMiAwIDAsMSAxMSwxNVYyMEEyLDIgMCAwLDEgOSwyMkEyLDIgMCAwLDEgNywyMFYxNU05LDdBMiwyIDAgMCwxIDcsNUEyLDIgMCAwLDEgOSwzQTIsMiAwIDAsMSAxMSw1VjdIOU05LDhBMiwyIDAgMCwxIDExLDEwQTIsMiAwIDAsMSA5LDEySDRBMiwyIDAgMCwxIDIsMTBBMiwyIDAgMCwxIDQsOEg5TTE3LDEwQTIsMiAwIDAsMSAxOSw4QTIsMiAwIDAsMSAyMSwxMEEyLDIgMCAwLDEgMTksMTJIMTdWMTBNMTYsMTBBMiwyIDAgMCwxIDE0LDEyQTIsMiAwIDAsMSAxMiwxMFY1QTIsMiAwIDAsMSAxNCwzQTIsMiAwIDAsMSAxNiw1VjEwTTE0LDE4QTIsMiAwIDAsMSAxNiwyMEEyLDIgMCAwLDEgMTQsMjJBMiwyIDAgMCwxIDEyLDIwVjE4SDE0TTE0LDE3QTIsMiAwIDAsMSAxMiwxNUEyLDIgMCAwLDEgMTQsMTNIMTlBMiwyIDAgMCwxIDIxLDE1QTIsMiAwIDAsMSAxOSwxN0gxNFoiIC8+PC9zdmc+"
|
||||
)
|
||||
|
||||
@ -59,8 +59,7 @@ import java.util.concurrent.TimeUnit;
|
||||
nodeDescription = "Process device messages based on device profile settings",
|
||||
nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " +
|
||||
"'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbDeviceProfileConfig"
|
||||
configDirective = "tbActionNodeDeviceProfileConfig"
|
||||
)
|
||||
public class TbDeviceProfileNode implements TbNode {
|
||||
|
||||
|
||||
@ -45,7 +45,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
configClazz = TbRabbitMqNodeConfiguration.class,
|
||||
nodeDescription = "Publish messages to the RabbitMQ",
|
||||
nodeDetails = "Will publish message payload to RabbitMQ queue.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeRabbitMqConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4="
|
||||
)
|
||||
|
||||
@ -45,7 +45,6 @@ import java.util.List;
|
||||
"For example <b>statusCode</b> field can be accessed with <code>metadata.statusCode</code>." +
|
||||
"<br/><b>Note-</b> if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," +
|
||||
"and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeRestApiCallConfig",
|
||||
iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+"
|
||||
)
|
||||
|
||||
@ -35,7 +35,6 @@ import java.util.UUID;
|
||||
configClazz = TbSendRestApiCallReplyNodeConfiguration.class,
|
||||
nodeDescription = "Sends reply to REST API call to rule engine",
|
||||
nodeDetails = "Expects messages with any message type. Forwards incoming message as a reply to REST API call sent to rule engine.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeSendRestApiCallReplyConfig",
|
||||
icon = "call_merge"
|
||||
)
|
||||
|
||||
@ -48,7 +48,6 @@ import java.util.UUID;
|
||||
configClazz = TbSendRpcReplyNodeConfiguration.class,
|
||||
nodeDescription = "Sends reply to RPC call from device",
|
||||
nodeDetails = "Expects messages with any message type. Will forward message body to the device.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeRpcReplyConfig",
|
||||
icon = "call_merge"
|
||||
)
|
||||
|
||||
@ -49,7 +49,6 @@ import java.util.concurrent.TimeUnit;
|
||||
nodeDescription = "Sends RPC call to device",
|
||||
nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes." +
|
||||
"If the RPC call request is originated by REST API call from user, will forward the response to user immediately.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeRpcRequestConfig",
|
||||
icon = "call_made"
|
||||
)
|
||||
|
||||
@ -35,7 +35,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
|
||||
configClazz = TbSendSmsNodeConfiguration.class,
|
||||
nodeDescription = "Sends SMS message via SMS provider.",
|
||||
nodeDetails = "Will send SMS message by populating target phone numbers and sms message fields using values derived from message metadata.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbExternalNodeSendSmsConfig",
|
||||
icon = "sms"
|
||||
)
|
||||
|
||||
@ -62,7 +62,6 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R
|
||||
"Additionally if checkbox <b>Send attributes updated notification</b> is set to true, rule node will put the \"Attributes Updated\" " +
|
||||
"event for <b>SHARED_SCOPE</b> and <b>SERVER_SCOPE</b> attributes updates to the corresponding rule engine queue." +
|
||||
"Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeAttributesConfig",
|
||||
icon = "file_upload"
|
||||
)
|
||||
|
||||
@ -45,7 +45,6 @@ import static org.thingsboard.server.common.data.DataConstants.SCOPE;
|
||||
" a key selected in the configuration, it will be ignored. If delete operation is completed successfully, " +
|
||||
" rule node will send the \"Attributes Deleted\" event to the root chain of the message originator and " +
|
||||
" send the incoming message via <b>Success</b> chain, otherwise, <b>Failure</b> chain is used.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeDeleteAttributesConfig",
|
||||
icon = "remove_circle"
|
||||
)
|
||||
|
||||
@ -58,7 +58,6 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE
|
||||
"However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. " +
|
||||
"The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. " +
|
||||
"So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbActionNodeTimeseriesConfig",
|
||||
icon = "file_upload"
|
||||
)
|
||||
|
||||
@ -34,7 +34,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
nodeDetails = "This node should be used together with \"synchronization end\" node. \n This node will put messages into queue based on message originator id. \n" +
|
||||
"Subsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\n" +
|
||||
"Size of the queue per originator and timeout values are configurable on a system level",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbNodeEmptyConfig")
|
||||
@Deprecated
|
||||
public class TbSynchronizationBeginNode implements TbNode {
|
||||
|
||||
@ -32,7 +32,6 @@ import org.thingsboard.server.common.msg.TbMsg;
|
||||
configClazz = EmptyNodeConfiguration.class,
|
||||
nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.",
|
||||
nodeDetails = "",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = ("tbNodeEmptyConfig")
|
||||
)
|
||||
@Deprecated
|
||||
|
||||
@ -54,7 +54,6 @@ import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED;
|
||||
"<li><strong>Entity by name pattern</strong> - specify entity type and name pattern of new originator. Following entity types are supported: " +
|
||||
"'Device', 'Asset', 'Entity View', 'Edge' or 'User'.</li></ul>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeChangeOriginatorConfig",
|
||||
icon = "find_replace"
|
||||
)
|
||||
|
||||
@ -46,7 +46,6 @@ import java.util.stream.Collectors;
|
||||
nodeDetails = "Copies key-value pairs from the message to message metadata, or vice-versa, according to the configured direction and keys. " +
|
||||
"Regular expressions can be used to define which keys-value pairs to copy. Any configured key not found in the source will be ignored.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeCopyKeysConfig",
|
||||
icon = "content_copy"
|
||||
)
|
||||
|
||||
@ -46,7 +46,6 @@ import java.util.stream.Collectors;
|
||||
nodeDetails = "Deletes key-value pairs from the message or message metadata according to the configured " +
|
||||
"keys and/or regular expressions.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeDeleteKeysConfig",
|
||||
icon = "remove_circle"
|
||||
)
|
||||
|
||||
@ -40,7 +40,6 @@ import java.util.concurrent.ExecutionException;
|
||||
nodeDescription = "Transforms incoming message body using JSONPath expression.",
|
||||
nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
icon = "functions",
|
||||
configDirective = "tbTransformationNodeJsonPathConfig"
|
||||
)
|
||||
|
||||
@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
|
||||
nodeDetails = "Renames keys in the message or message metadata according to the provided mapping. " +
|
||||
"If key to rename doesn't exist in the specified source (message or message metadata) it will be ignored.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeRenameKeysConfig",
|
||||
icon = "find_replace"
|
||||
)
|
||||
|
||||
@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
|
||||
nodeDetails = "Splits an array message into individual elements, with each element sent as a separate message. " +
|
||||
"All outbound messages will have the same type and metadata as the original array message.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
icon = "content_copy",
|
||||
configDirective = "tbNodeEmptyConfig"
|
||||
)
|
||||
|
||||
@ -41,7 +41,6 @@ import java.util.List;
|
||||
"<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>   metadata: <i style=\"color: #666;\">new metadata</i>,<br/>   msgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" +
|
||||
"All fields in resulting object are optional and will be taken from original message if not specified.<br><br>" +
|
||||
"Output connections: <code>Success</code>, <code>Failure</code>.",
|
||||
uiResources = {"static/rulenode/rulenode-core-config.js"},
|
||||
configDirective = "tbTransformationNodeScriptConfig"
|
||||
)
|
||||
public class TbTransformMsgNode extends TbAbstractTransformNode<TbTransformMsgNodeConfiguration> {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -180,6 +180,10 @@ export class RuleChainService {
|
||||
return this.http.post<TestScriptResult>(url, inputParams, defaultHttpOptionsFromConfig(config));
|
||||
}
|
||||
|
||||
public registerSystemRuleNodeConfigModule(module: any) {
|
||||
Object.assign(this.ruleNodeConfigComponents, this.resourcesService.extractComponentsFromModule<IRuleNodeConfigurationComponent>(module, true));
|
||||
}
|
||||
|
||||
private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> {
|
||||
return this.componentDescriptorService.getComponentDescriptorsByTypes(ruleNodeTypeComponentTypes, ruleChainType, config).pipe(
|
||||
map((components) => {
|
||||
@ -211,7 +215,7 @@ export class RuleChainService {
|
||||
Observable<RuleNodeComponentDescriptor> {
|
||||
const nodeDefinition = component.configurationDescriptor.nodeDefinition;
|
||||
const uiResources = nodeDefinition.uiResources;
|
||||
if (uiResources && uiResources.length) {
|
||||
if (!this.ruleNodeConfigComponents[nodeDefinition.configDirective] && uiResources && uiResources.length) {
|
||||
const commonResources = uiResources.filter((resource) => !resource.endsWith('.js'));
|
||||
const moduleResource = uiResources.find((resource) => resource.endsWith('.js'));
|
||||
const tasks: Observable<any>[] = [];
|
||||
|
||||
@ -31,7 +31,7 @@ import { forkJoin, from, Observable, ReplaySubject, throwError } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { IModulesMap } from '@modules/common/modules-map.models';
|
||||
import { TbResourceId } from '@shared/models/id/tb-resource-id';
|
||||
import { isObject } from '@core/utils';
|
||||
import { camelCase, isObject } from '@core/utils';
|
||||
import { AuthService } from '@core/auth/auth.service';
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import { selectIsAuthenticated } from '@core/auth/auth.selectors';
|
||||
@ -51,6 +51,8 @@ export interface ModulesWithComponents {
|
||||
standaloneComponents: ɵComponentDef<any>[];
|
||||
}
|
||||
|
||||
export type ComponentsSelectorMap<T> = Record<string, Type<T>>;
|
||||
|
||||
export const flatModulesWithComponents = (modulesWithComponentsList: ModulesWithComponents[]): ModulesWithComponents => {
|
||||
const modulesWithComponents: ModulesWithComponents = {
|
||||
modules: [],
|
||||
@ -91,6 +93,17 @@ export const componentTypeBySelector = (modulesWithComponents: ModulesWithCompon
|
||||
const matchesSelector = (selectors: ɵCssSelectorList, selector: string) =>
|
||||
selectors.some(s => s.some(s1 => typeof s1 === 'string' && s1 === selector));
|
||||
|
||||
const extractSelectorFromComponent = (comp: ɵComponentDef<any>): string => {
|
||||
for (const selectors of comp.selectors) {
|
||||
for (const selector of selectors) {
|
||||
if (typeof selector === 'string') {
|
||||
return selector;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@ -252,6 +265,27 @@ export class ResourcesService {
|
||||
);
|
||||
}
|
||||
|
||||
public extractComponentsFromModule<T>(module: any, isCamelCaseSelector = false): ComponentsSelectorMap<T> {
|
||||
const modulesWithComponents = this.extractModulesWithComponents(module);
|
||||
const componentMap: ComponentsSelectorMap<T> = {};
|
||||
|
||||
const processComponents = (components: Array<ɵComponentDef<T>>) => {
|
||||
components.forEach(item => {
|
||||
let selector = extractSelectorFromComponent(item);
|
||||
if (isCamelCaseSelector) {
|
||||
selector = camelCase(selector);
|
||||
}
|
||||
componentMap[selector] = item.type;
|
||||
});
|
||||
};
|
||||
|
||||
processComponents(modulesWithComponents.standaloneComponents);
|
||||
|
||||
modulesWithComponents.modules.forEach(module => {
|
||||
processComponents(module.components);
|
||||
})
|
||||
return componentMap;
|
||||
}
|
||||
|
||||
private extractModulesWithComponents(module: any,
|
||||
modulesWithComponents: ModulesWithComponents = {
|
||||
@ -284,7 +318,7 @@ export class ResourcesService {
|
||||
modulesWithComponents.standaloneComponents.push(component);
|
||||
}
|
||||
} else {
|
||||
this.extractModulesWithComponents(module, modulesWithComponents, visitedModules);
|
||||
this.extractModulesWithComponents(element, modulesWithComponents, visitedModules);
|
||||
}
|
||||
}
|
||||
} else if (ɵNG_COMP_DEF in module) {
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '@shared/public-api';
|
||||
import { HomeComponentsModule } from '@home/components/public-api';
|
||||
import { AttributesConfigComponent } from './attributes-config.component';
|
||||
import { TimeseriesConfigComponent } from './timeseries-config.component';
|
||||
import { RpcRequestConfigComponent } from './rpc-request-config.component';
|
||||
import { LogConfigComponent } from './log-config.component';
|
||||
import { AssignCustomerConfigComponent } from './assign-customer-config.component';
|
||||
import { ClearAlarmConfigComponent } from './clear-alarm-config.component';
|
||||
import { CreateAlarmConfigComponent } from './create-alarm-config.component';
|
||||
import { CreateRelationConfigComponent } from './create-relation-config.component';
|
||||
import { MsgDelayConfigComponent } from './msg-delay-config.component';
|
||||
import { DeleteRelationConfigComponent } from './delete-relation-config.component';
|
||||
import { GeneratorConfigComponent } from './generator-config.component';
|
||||
import { GpsGeoActionConfigComponent } from './gps-geo-action-config.component';
|
||||
import { MsgCountConfigComponent } from './msg-count-config.component';
|
||||
import { RpcReplyConfigComponent } from './rpc-reply-config.component';
|
||||
import { SaveToCustomTableConfigComponent } from './save-to-custom-table-config.component';
|
||||
import { CommonRuleNodeConfigModule } from '../common/common-rule-node-config.module';
|
||||
import { UnassignCustomerConfigComponent } from './unassign-customer-config.component';
|
||||
import { DeviceProfileConfigComponent } from './device-profile-config.component';
|
||||
import { PushToEdgeConfigComponent } from './push-to-edge-config.component';
|
||||
import { PushToCloudConfigComponent } from './push-to-cloud-config.component';
|
||||
import { DeleteAttributesConfigComponent } from './delete-attributes-config.component';
|
||||
import { MathFunctionConfigComponent } from './math-function-config.component';
|
||||
import { DeviceStateConfigComponent } from './device-state-config.component';
|
||||
import { SendRestApiCallReplyConfigComponent } from './send-rest-api-call-reply-config.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DeleteAttributesConfigComponent,
|
||||
AttributesConfigComponent,
|
||||
TimeseriesConfigComponent,
|
||||
RpcRequestConfigComponent,
|
||||
LogConfigComponent,
|
||||
AssignCustomerConfigComponent,
|
||||
ClearAlarmConfigComponent,
|
||||
CreateAlarmConfigComponent,
|
||||
CreateRelationConfigComponent,
|
||||
MsgDelayConfigComponent,
|
||||
DeleteRelationConfigComponent,
|
||||
GeneratorConfigComponent,
|
||||
GpsGeoActionConfigComponent,
|
||||
MsgCountConfigComponent,
|
||||
RpcReplyConfigComponent,
|
||||
SaveToCustomTableConfigComponent,
|
||||
UnassignCustomerConfigComponent,
|
||||
SendRestApiCallReplyConfigComponent,
|
||||
DeviceProfileConfigComponent,
|
||||
PushToEdgeConfigComponent,
|
||||
PushToCloudConfigComponent,
|
||||
MathFunctionConfigComponent,
|
||||
DeviceStateConfigComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
HomeComponentsModule,
|
||||
CommonRuleNodeConfigModule
|
||||
],
|
||||
exports: [
|
||||
DeleteAttributesConfigComponent,
|
||||
AttributesConfigComponent,
|
||||
TimeseriesConfigComponent,
|
||||
RpcRequestConfigComponent,
|
||||
LogConfigComponent,
|
||||
AssignCustomerConfigComponent,
|
||||
ClearAlarmConfigComponent,
|
||||
CreateAlarmConfigComponent,
|
||||
CreateRelationConfigComponent,
|
||||
MsgDelayConfigComponent,
|
||||
DeleteRelationConfigComponent,
|
||||
GeneratorConfigComponent,
|
||||
GpsGeoActionConfigComponent,
|
||||
MsgCountConfigComponent,
|
||||
RpcReplyConfigComponent,
|
||||
SaveToCustomTableConfigComponent,
|
||||
UnassignCustomerConfigComponent,
|
||||
SendRestApiCallReplyConfigComponent,
|
||||
DeviceProfileConfigComponent,
|
||||
PushToEdgeConfigComponent,
|
||||
PushToCloudConfigComponent,
|
||||
MathFunctionConfigComponent,
|
||||
DeviceStateConfigComponent
|
||||
]
|
||||
})
|
||||
export class ActionRuleNodeConfigModule {
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="assignCustomerConfigForm" class="flex flex-col">
|
||||
<div class="tb-form-panel no-padding no-border">
|
||||
<mat-form-field class="mat-block" subscriptSizing="dynamic">
|
||||
<mat-label translate>rule-node-config.customer-name-pattern</mat-label>
|
||||
<input required matInput formControlName="customerNamePattern">
|
||||
<mat-error *ngIf="assignCustomerConfigForm.get('customerNamePattern').hasError('required') ||
|
||||
assignCustomerConfigForm.get('customerNamePattern').hasError('pattern')">
|
||||
{{ 'rule-node-config.customer-name-pattern-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-hint translate>rule-node-config.customer-name-pattern-hint</mat-hint>
|
||||
</mat-form-field>
|
||||
<div class="tb-form-row">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="createCustomerIfNotExists">
|
||||
{{ 'rule-node-config.create-customer-if-not-exists' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -0,0 +1,49 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-assign-to-customer-config',
|
||||
templateUrl: './assign-customer-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class AssignCustomerConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
assignCustomerConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.assignCustomerConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.assignCustomerConfigForm = this.fb.group({
|
||||
customerNamePattern: [configuration ? configuration.customerNamePattern : null, [Validators.required, Validators.pattern(/.*\S.*/)]],
|
||||
createCustomerIfNotExists: [configuration ? configuration.createCustomerIfNotExists : false, []]
|
||||
});
|
||||
}
|
||||
|
||||
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
configuration.customerNamePattern = configuration.customerNamePattern.trim();
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="attributesConfigForm" class="tb-form-panel no-border no-padding">
|
||||
<div class="tb-form-panel stroked">
|
||||
<tb-example-hint [hintText]="'rule-node-config.attributes-scope-hint'">
|
||||
</tb-example-hint>
|
||||
<div class="tb-form-row no-border no-padding tb-standard-fields">
|
||||
<mat-form-field class="flex">
|
||||
<mat-label>{{ 'rule-node-config.attributes-scope' | translate }}</mat-label>
|
||||
<mat-select required
|
||||
class="tb-entity-type-select" matInput formControlName="scope">
|
||||
<mat-option *ngFor="let scope of attributeScopes" [value]="scope">
|
||||
{{ telemetryTypeTranslationsMap.get(scope) | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex">
|
||||
<mat-label>{{ 'rule-node-config.attributes-scope-value' | translate }}</mat-label>
|
||||
<input type="text" matInput readonly disabled [ngModel]="attributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}">
|
||||
<button type="button"
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="Copy"
|
||||
ngxClipboard
|
||||
[cbContent]="attributesConfigForm.get('scope').value"
|
||||
matTooltip="{{ 'rule-node-config.attributes-scope-value-copy' | translate }}">
|
||||
<mat-icon aria-hidden="false"
|
||||
aria-label="help-icon">content_copy
|
||||
</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="tb-form-panel stroked">
|
||||
<mat-expansion-panel class="tb-settings">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div tb-hint-tooltip-icon="{{ (attributesConfigForm.get('updateAttributesOnlyOnValueChange').value
|
||||
? 'rule-node-config.update-attributes-only-on-value-change-hint-enabled'
|
||||
: 'rule-node-config.update-attributes-only-on-value-change-hint') | translate }}"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="updateAttributesOnlyOnValueChange">
|
||||
{{ 'rule-node-config.update-attributes-only-on-value-change' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.send-attributes-updated-notification-hint' | translate }}"
|
||||
*ngIf="attributesConfigForm.get('scope').value !== attributeScopeMap.CLIENT_SCOPE"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesUpdatedNotification">
|
||||
{{ 'rule-node-config.send-attributes-updated-notification' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.notify-device-on-update-hint' | translate }}"
|
||||
*ngIf="attributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice">
|
||||
{{ 'rule-node-config.notify-device' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,65 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
|
||||
import { AttributeScope, telemetryTypeTranslations } from '@app/shared/models/telemetry/telemetry.models';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-attributes-config',
|
||||
templateUrl: './attributes-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class AttributesConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
attributeScopeMap = AttributeScope;
|
||||
attributeScopes = Object.keys(AttributeScope);
|
||||
telemetryTypeTranslationsMap = telemetryTypeTranslations;
|
||||
|
||||
attributesConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.attributesConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.attributesConfigForm = this.fb.group({
|
||||
scope: [configuration ? configuration.scope : null, [Validators.required]],
|
||||
notifyDevice: [configuration ? configuration.notifyDevice : true, []],
|
||||
sendAttributesUpdatedNotification: [configuration ? configuration.sendAttributesUpdatedNotification : false, []],
|
||||
updateAttributesOnlyOnValueChange: [configuration ? configuration.updateAttributesOnlyOnValueChange : false, []]
|
||||
});
|
||||
|
||||
this.attributesConfigForm.get('scope').valueChanges.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe((value) => {
|
||||
if (value !== AttributeScope.SHARED_SCOPE) {
|
||||
this.attributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false});
|
||||
}
|
||||
if (value === AttributeScope.CLIENT_SCOPE) {
|
||||
this.attributesConfigForm.get('sendAttributesUpdatedNotification').patchValue(false, {emitEvent: false});
|
||||
}
|
||||
this.attributesConfigForm.get('updateAttributesOnlyOnValueChange').patchValue(false, {emitEvent: false});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="clearAlarmConfigForm" class="flex flex-col">
|
||||
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang>
|
||||
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS"
|
||||
#jsFuncComponent
|
||||
formControlName="alarmDetailsBuildJs"
|
||||
functionName="Details"
|
||||
[functionArgs]="['msg', 'metadata', 'msgType']"
|
||||
helpId="rulenode/clear_alarm_node_script_fn"
|
||||
noValidate="true">
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL"
|
||||
#tbelFuncComponent
|
||||
formControlName="alarmDetailsBuildTbel"
|
||||
functionName="Details"
|
||||
[functionArgs]="['msg', 'metadata', 'msgType']"
|
||||
[disableUndefinedCheck]="true"
|
||||
[scriptLanguage]="scriptLanguage.TBEL"
|
||||
helpId="rulenode/tbel/clear_alarm_node_script_fn"
|
||||
noValidate="true">
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<div class="flex flex-row" style="padding-bottom: 16px;">
|
||||
<button mat-button mat-raised-button color="primary" (click)="testScript()">
|
||||
{{ testScriptLabel | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<mat-form-field class="mat-block" subscriptSizing="dynamic">
|
||||
<mat-label translate>rule-node-config.alarm-type</mat-label>
|
||||
<input required matInput formControlName="alarmType">
|
||||
<mat-error *ngIf="clearAlarmConfigForm.get('alarmType').hasError('required')">
|
||||
{{ 'rule-node-config.alarm-type-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
@ -0,0 +1,127 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
RuleNodeConfiguration,
|
||||
RuleNodeConfigurationComponent,
|
||||
ScriptLanguage
|
||||
} from '@app/shared/models/rule-node.models';
|
||||
import type { JsFuncComponent } from '@app/shared/components/js-func.component';
|
||||
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-clear-alarm-config',
|
||||
templateUrl: './clear-alarm-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class ClearAlarmConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent;
|
||||
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent;
|
||||
|
||||
clearAlarmConfigForm: UntypedFormGroup;
|
||||
|
||||
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled;
|
||||
|
||||
scriptLanguage = ScriptLanguage;
|
||||
|
||||
changeScript: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
readonly hasScript = true;
|
||||
|
||||
readonly testScriptLabel = 'rule-node-config.test-details-function';
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private fb: UntypedFormBuilder,
|
||||
private nodeScriptTestService: NodeScriptTestService,
|
||||
private translate: TranslateService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.clearAlarmConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.clearAlarmConfigForm = this.fb.group({
|
||||
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]],
|
||||
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []],
|
||||
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []],
|
||||
alarmType: [configuration ? configuration.alarmType : null, [Validators.required]]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['scriptLang'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
let scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) {
|
||||
scriptLang = ScriptLanguage.JS;
|
||||
this.clearAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false});
|
||||
setTimeout(() => {this.clearAlarmConfigForm.updateValueAndValidity({emitEvent: true});});
|
||||
}
|
||||
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []);
|
||||
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent});
|
||||
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []);
|
||||
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
if (configuration) {
|
||||
if (!configuration.scriptLang) {
|
||||
configuration.scriptLang = ScriptLanguage.JS;
|
||||
}
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
testScript(debugEventBody?: DebugRuleNodeEventBody) {
|
||||
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value;
|
||||
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel';
|
||||
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/clear_alarm_node_script_fn' : 'rulenode/tbel/clear_alarm_node_script_fn';
|
||||
const script: string = this.clearAlarmConfigForm.get(scriptField).value;
|
||||
this.nodeScriptTestService.testNodeScript(
|
||||
script,
|
||||
'json',
|
||||
this.translate.instant('rule-node-config.details'),
|
||||
'Details',
|
||||
['msg', 'metadata', 'msgType'],
|
||||
this.ruleNodeId,
|
||||
helpId,
|
||||
scriptLang,
|
||||
debugEventBody
|
||||
).subscribe((theScript) => {
|
||||
if (theScript) {
|
||||
this.clearAlarmConfigForm.get(scriptField).setValue(theScript);
|
||||
this.changeScript.emit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected onValidate() {
|
||||
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.JS) {
|
||||
this.jsFuncComponent.validateOnSubmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="createAlarmConfigForm" class="flex flex-col">
|
||||
<mat-checkbox formControlName="useMessageAlarmData">
|
||||
{{ 'rule-node-config.use-message-alarm-data' | translate }}
|
||||
</mat-checkbox>
|
||||
<mat-checkbox formControlName="overwriteAlarmDetails" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === true">
|
||||
{{ 'rule-node-config.overwrite-alarm-details' | translate }}
|
||||
</mat-checkbox>
|
||||
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false ||
|
||||
createAlarmConfigForm.get('overwriteAlarmDetails').value === true">
|
||||
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang>
|
||||
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS"
|
||||
#jsFuncComponent
|
||||
formControlName="alarmDetailsBuildJs"
|
||||
functionName="Details"
|
||||
[functionArgs]="['msg', 'metadata', 'msgType']"
|
||||
helpId="rulenode/create_alarm_node_script_fn"
|
||||
noValidate="true">
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL"
|
||||
#tbelFuncComponent
|
||||
formControlName="alarmDetailsBuildTbel"
|
||||
functionName="Details"
|
||||
[functionArgs]="['msg', 'metadata', 'msgType']"
|
||||
[disableUndefinedCheck]="true"
|
||||
[scriptLanguage]="scriptLanguage.TBEL"
|
||||
helpId="rulenode/tbel/create_alarm_node_script_fn"
|
||||
noValidate="true">
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<div class="flex flex-row" style="padding-bottom: 16px;">
|
||||
<button mat-button mat-raised-button color="primary" (click)="testScript()">
|
||||
{{ testScriptLabel | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false">
|
||||
<mat-form-field class="flex-1" subscriptSizing="dynamic">
|
||||
<mat-label translate>rule-node-config.alarm-type</mat-label>
|
||||
<input required matInput formControlName="alarmType">
|
||||
<mat-error *ngIf="createAlarmConfigForm.get('alarmType').hasError('required')">
|
||||
{{ 'rule-node-config.alarm-type-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-checkbox formControlName="dynamicSeverity">
|
||||
{{ 'rule-node-config.use-alarm-severity-pattern' | translate }}
|
||||
</mat-checkbox>
|
||||
<mat-form-field class="flex-1" *ngIf="!createAlarmConfigForm.get('dynamicSeverity').value">
|
||||
<mat-label translate>rule-node-config.alarm-severity</mat-label>
|
||||
<mat-select formControlName="severity" required>
|
||||
<mat-option *ngFor="let severity of alarmSeverities" [value]="severity">
|
||||
{{ alarmSeverityTranslationMap.get(severity) | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')">
|
||||
{{ 'rule-node-config.alarm-severity-required' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-1" *ngIf="createAlarmConfigForm.get('dynamicSeverity').value" subscriptSizing="dynamic">
|
||||
<mat-label translate>rule-node-config.alarm-severity-pattern</mat-label>
|
||||
<input matInput formControlName="severity" required>
|
||||
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')">
|
||||
{{ 'rule-node-config.alarm-severity-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-hint [innerHTML]="'rule-node-config.alarm-severity-pattern-hint' | translate | safe: 'html'"></mat-hint>
|
||||
</mat-form-field>
|
||||
<mat-checkbox formControlName="propagate">
|
||||
{{ 'rule-node-config.propagate' | translate }}
|
||||
</mat-checkbox>
|
||||
<section *ngIf="createAlarmConfigForm.get('propagate').value === true">
|
||||
<mat-form-field floatLabel="always" class="mat-block" subscriptSizing="dynamic">
|
||||
<mat-label translate>rule-node-config.relation-types-list</mat-label>
|
||||
<mat-chip-grid #relationTypesChipList>
|
||||
<mat-chip-row
|
||||
*ngFor="let key of createAlarmConfigForm.get('relationTypes').value;"
|
||||
(removed)="removeKey(key, 'relationTypes')">
|
||||
{{key}}
|
||||
<mat-icon matChipRemove>close</mat-icon>
|
||||
</mat-chip-row>
|
||||
<input matInput type="text" placeholder="{{'rule-node-config.relation-types-list' | translate}}"
|
||||
[matChipInputFor]="relationTypesChipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="addKey($event, 'relationTypes')"
|
||||
[matChipInputAddOnBlur]="true">
|
||||
</mat-chip-grid>
|
||||
<mat-hint translate>rule-node-config.relation-types-list-hint</mat-hint>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
<mat-checkbox formControlName="propagateToOwner">
|
||||
{{ 'rule-node-config.propagate-to-owner' | translate }}
|
||||
</mat-checkbox>
|
||||
<mat-checkbox formControlName="propagateToTenant">
|
||||
{{ 'rule-node-config.propagate-to-tenant' | translate }}
|
||||
</mat-checkbox>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,202 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
|
||||
import { MatChipInputEvent } from '@angular/material/chips';
|
||||
import {
|
||||
RuleNodeConfiguration,
|
||||
RuleNodeConfigurationComponent,
|
||||
ScriptLanguage
|
||||
} from '@app/shared/models/rule-node.models';
|
||||
import type { JsFuncComponent } from '@app/shared/components/js-func.component';
|
||||
import { AlarmSeverity, alarmSeverityTranslations } from '@app/shared/models/alarm.models';
|
||||
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-create-alarm-config',
|
||||
templateUrl: './create-alarm-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent;
|
||||
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent;
|
||||
|
||||
alarmSeverities = Object.keys(AlarmSeverity);
|
||||
alarmSeverityTranslationMap = alarmSeverityTranslations;
|
||||
createAlarmConfigForm: UntypedFormGroup;
|
||||
|
||||
separatorKeysCodes = [ENTER, COMMA, SEMICOLON];
|
||||
|
||||
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled;
|
||||
|
||||
scriptLanguage = ScriptLanguage;
|
||||
|
||||
changeScript: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
readonly hasScript = true;
|
||||
|
||||
readonly testScriptLabel = 'rule-node-config.test-details-function';
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private fb: UntypedFormBuilder,
|
||||
private nodeScriptTestService: NodeScriptTestService,
|
||||
private translate: TranslateService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.createAlarmConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.createAlarmConfigForm = this.fb.group({
|
||||
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]],
|
||||
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []],
|
||||
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []],
|
||||
useMessageAlarmData: [configuration ? configuration.useMessageAlarmData : false, []],
|
||||
overwriteAlarmDetails: [configuration ? configuration.overwriteAlarmDetails : false, []],
|
||||
alarmType: [configuration ? configuration.alarmType : null, []],
|
||||
severity: [configuration ? configuration.severity : null, []],
|
||||
propagate: [configuration ? configuration.propagate : false, []],
|
||||
relationTypes: [configuration ? configuration.relationTypes : null, []],
|
||||
propagateToOwner: [configuration ? configuration.propagateToOwner : false, []],
|
||||
propagateToTenant: [configuration ? configuration.propagateToTenant : false, []],
|
||||
dynamicSeverity: false
|
||||
});
|
||||
|
||||
this.createAlarmConfigForm.get('dynamicSeverity').valueChanges.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe((dynamicSeverity) => {
|
||||
if(dynamicSeverity){
|
||||
this.createAlarmConfigForm.get('severity').patchValue('',{emitEvent:false});
|
||||
} else {
|
||||
this.createAlarmConfigForm.get('severity').patchValue(this.alarmSeverities[0],{emitEvent:false});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['useMessageAlarmData', 'overwriteAlarmDetails', 'scriptLang'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value;
|
||||
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value;
|
||||
if (useMessageAlarmData) {
|
||||
this.createAlarmConfigForm.get('alarmType').setValidators([]);
|
||||
this.createAlarmConfigForm.get('severity').setValidators([]);
|
||||
} else {
|
||||
this.createAlarmConfigForm.get('alarmType').setValidators([Validators.required]);
|
||||
this.createAlarmConfigForm.get('severity').setValidators([Validators.required]);
|
||||
}
|
||||
this.createAlarmConfigForm.get('alarmType').updateValueAndValidity({emitEvent});
|
||||
this.createAlarmConfigForm.get('severity').updateValueAndValidity({emitEvent});
|
||||
|
||||
let scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) {
|
||||
scriptLang = ScriptLanguage.JS;
|
||||
this.createAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false});
|
||||
setTimeout(() => {this.createAlarmConfigForm.updateValueAndValidity({emitEvent: true});});
|
||||
}
|
||||
const useAlarmDetailsBuildScript = useMessageAlarmData === false || overwriteAlarmDetails === true;
|
||||
this.createAlarmConfigForm.get('alarmDetailsBuildJs')
|
||||
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.JS ? [Validators.required] : []);
|
||||
this.createAlarmConfigForm.get('alarmDetailsBuildTbel')
|
||||
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []);
|
||||
this.createAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent});
|
||||
this.createAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
if (configuration) {
|
||||
if (!configuration.scriptLang) {
|
||||
configuration.scriptLang = ScriptLanguage.JS;
|
||||
}
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
testScript(debugEventBody?: DebugRuleNodeEventBody) {
|
||||
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value;
|
||||
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel';
|
||||
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/create_alarm_node_script_fn' : 'rulenode/tbel/create_alarm_node_script_fn';
|
||||
const script: string = this.createAlarmConfigForm.get(scriptField).value;
|
||||
this.nodeScriptTestService.testNodeScript(
|
||||
script,
|
||||
'json',
|
||||
this.translate.instant('rule-node-config.details'),
|
||||
'Details',
|
||||
['msg', 'metadata', 'msgType'],
|
||||
this.ruleNodeId,
|
||||
helpId,
|
||||
scriptLang,
|
||||
debugEventBody
|
||||
).subscribe((theScript) => {
|
||||
if (theScript) {
|
||||
this.createAlarmConfigForm.get(scriptField).setValue(theScript);
|
||||
this.changeScript.emit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeKey(key: string, keysField: string): void {
|
||||
const keys: string[] = this.createAlarmConfigForm.get(keysField).value;
|
||||
const index = keys.indexOf(key);
|
||||
if (index >= 0) {
|
||||
keys.splice(index, 1);
|
||||
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true});
|
||||
}
|
||||
}
|
||||
|
||||
addKey(event: MatChipInputEvent, keysField: string): void {
|
||||
const input = event.input;
|
||||
let value = event.value;
|
||||
if ((value || '').trim()) {
|
||||
value = value.trim();
|
||||
let keys: string[] = this.createAlarmConfigForm.get(keysField).value;
|
||||
if (!keys || keys.indexOf(value) === -1) {
|
||||
if (!keys) {
|
||||
keys = [];
|
||||
}
|
||||
keys.push(value);
|
||||
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true});
|
||||
}
|
||||
}
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
protected onValidate() {
|
||||
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value;
|
||||
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value;
|
||||
if (!useMessageAlarmData || overwriteAlarmDetails) {
|
||||
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.JS) {
|
||||
this.jsFuncComponent.validateOnSubmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,100 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="createRelationConfigForm" class="tb-form-panel no-padding no-border">
|
||||
<div class="tb-form-panel stroked no-padding-bottom">
|
||||
<div class="tb-form-panel-title" translate>rule-node-config.relation-parameters</div>
|
||||
<div class="flex flex-col">
|
||||
<mat-form-field class="mat-block" hideRequiredMarker>
|
||||
<mat-label translate>relation.direction</mat-label>
|
||||
<mat-select required matInput formControlName="direction">
|
||||
<mat-option *ngFor="let type of directionTypes" [value]="type">
|
||||
{{ directionTypeTranslations.get(type) | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<tb-relation-type-autocomplete
|
||||
required
|
||||
formControlName="relationType">
|
||||
</tb-relation-type-autocomplete>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tb-form-panel stroked no-padding-bottom">
|
||||
<div class="tb-form-panel-title" translate>rule-node-config.target-entity</div>
|
||||
<div class="flex flex-row gap-4">
|
||||
<tb-entity-type-select
|
||||
class="flex-1"
|
||||
showLabel
|
||||
required
|
||||
[allowedEntityTypes]="allowedEntityTypes"
|
||||
formControlName="entityType">
|
||||
</tb-entity-type-select>
|
||||
|
||||
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value &&
|
||||
createRelationConfigForm.get('entityType').value !== entityType.TENANT"
|
||||
class="mat-block flex-1">
|
||||
<mat-label>{{ entityTypeNamePatternTranslation.get(createRelationConfigForm.get('entityType').value) | translate }}</mat-label>
|
||||
<input required matInput formControlName="entityNamePattern">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value === entityType.DEVICE ||
|
||||
createRelationConfigForm.get('entityType').value === entityType.ASSET"
|
||||
class="mat-block flex-1">
|
||||
<mat-label translate>rule-node-config.profile-name</mat-label>
|
||||
<input matInput formControlName="entityTypePattern">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<tb-example-hint [hintText]="'rule-node-config.kv-map-pattern-hint'"
|
||||
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER ||
|
||||
createRelationConfigForm.get('entityType').value === entityType.DEVICE ||
|
||||
createRelationConfigForm.get('entityType').value === entityType.ASSET"></tb-example-hint>
|
||||
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.create-entity-if-not-exists-hint' | translate }}"
|
||||
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER ||
|
||||
createRelationConfigForm.get('entityType').value === entityType.DEVICE ||
|
||||
createRelationConfigForm.get('entityType').value === entityType.ASSET"
|
||||
style="margin-bottom: 18px"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="createEntityIfNotExists">
|
||||
{{ 'rule-node-config.create-entity-if-not-exists' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
<section class="tb-form-panel stroked no-padding">
|
||||
<mat-expansion-panel class="tb-settings">
|
||||
<mat-expansion-panel-header style="padding: 16px">
|
||||
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="tb-form-panel no-border no-padding-top">
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.remove-current-relations-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="removeCurrentRelations">
|
||||
{{ 'rule-node-config.remove-current-relations' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.change-originator-to-related-entity-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="changeOriginatorToRelatedEntity">
|
||||
{{ 'rule-node-config.change-originator-to-related-entity' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,107 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models';
|
||||
import { EntitySearchDirection } from '@app/shared/models/relation.models';
|
||||
import { EntityType } from '@app/shared/models/entity-type.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-create-relation-config',
|
||||
templateUrl: './create-relation-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class CreateRelationConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
directionTypes = Object.keys(EntitySearchDirection);
|
||||
directionTypeTranslations = new Map<EntitySearchDirection, string>(
|
||||
[
|
||||
[EntitySearchDirection.FROM, 'rule-node-config.search-direction-from'],
|
||||
[EntitySearchDirection.TO, 'rule-node-config.search-direction-to'],
|
||||
]
|
||||
);
|
||||
|
||||
entityType = EntityType;
|
||||
|
||||
entityTypeNamePatternTranslation = new Map<EntityType, string>(
|
||||
[
|
||||
[EntityType.DEVICE, 'rule-node-config.device-name-pattern'],
|
||||
[EntityType.ASSET, 'rule-node-config.asset-name-pattern'],
|
||||
[EntityType.ENTITY_VIEW, 'rule-node-config.entity-view-name-pattern'],
|
||||
[EntityType.CUSTOMER, 'rule-node-config.customer-title-pattern'],
|
||||
[EntityType.USER, 'rule-node-config.user-name-pattern'],
|
||||
[EntityType.DASHBOARD, 'rule-node-config.dashboard-name-pattern'],
|
||||
[EntityType.EDGE, 'rule-node-config.edge-name-pattern']
|
||||
]
|
||||
);
|
||||
|
||||
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT,
|
||||
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE];
|
||||
|
||||
createRelationConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.createRelationConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.createRelationConfigForm = this.fb.group({
|
||||
direction: [configuration ? configuration.direction : null, [Validators.required]],
|
||||
entityType: [configuration ? configuration.entityType : null, [Validators.required]],
|
||||
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []],
|
||||
entityTypePattern: [configuration ? configuration.entityTypePattern : null, []],
|
||||
relationType: [configuration ? configuration.relationType : null, [Validators.required]],
|
||||
createEntityIfNotExists: [configuration ? configuration.createEntityIfNotExists : false, []],
|
||||
removeCurrentRelations: [configuration ? configuration.removeCurrentRelations : false, []],
|
||||
changeOriginatorToRelatedEntity: [configuration ? configuration.changeOriginatorToRelatedEntity : false, []]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['entityType', 'createEntityIfNotExists'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
const entityType: EntityType = this.createRelationConfigForm.get('entityType').value;
|
||||
if (entityType) {
|
||||
this.createRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]);
|
||||
} else {
|
||||
this.createRelationConfigForm.get('entityNamePattern').setValidators([]);
|
||||
}
|
||||
if (entityType && (entityType === EntityType.DEVICE || entityType === EntityType.ASSET)) {
|
||||
const validators = [Validators.pattern(/.*\S.*/)]
|
||||
if (this.createRelationConfigForm.get('createEntityIfNotExists').value) {
|
||||
validators.push(Validators.required);
|
||||
}
|
||||
this.createRelationConfigForm.get('entityTypePattern').setValidators(validators);
|
||||
} else {
|
||||
this.createRelationConfigForm.get('entityTypePattern').setValidators([]);
|
||||
}
|
||||
this.createRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent});
|
||||
this.createRelationConfigForm.get('entityTypePattern').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null;
|
||||
configuration.entityTypePattern = configuration.entityTypePattern ? configuration.entityTypePattern.trim() : null;
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="deleteAttributesConfigForm" class="tb-form-panel no-border no-padding">
|
||||
<div class="tb-form-panel stroked">
|
||||
<tb-example-hint [hintText]="'rule-node-config.attributes-scope-hint'">
|
||||
</tb-example-hint>
|
||||
<div class="tb-form-row no-border no-padding tb-standard-fields">
|
||||
<mat-form-field class="flex">
|
||||
<mat-label>{{ 'rule-node-config.attributes-scope' | translate }}</mat-label>
|
||||
<mat-select required
|
||||
class="tb-entity-type-select" matInput formControlName="scope">
|
||||
<mat-option *ngFor="let scope of attributeScopes" [value]="scope">
|
||||
{{ telemetryTypeTranslationsMap.get(scope) | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex">
|
||||
<mat-label>{{ 'rule-node-config.attributes-scope-value' | translate }}</mat-label>
|
||||
<input type="text" matInput readonly disabled [ngModel]="deleteAttributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}">
|
||||
<button type="button"
|
||||
matSuffix
|
||||
mat-icon-button
|
||||
aria-label="Copy"
|
||||
ngxClipboard
|
||||
[cbContent]="deleteAttributesConfigForm.get('scope').value"
|
||||
matTooltip="{{ 'rule-node-config.attributes-scope-value-copy' | translate }}">
|
||||
<mat-icon aria-hidden="false"
|
||||
aria-label="help-icon">content_copy
|
||||
</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field class="mat-block" subscriptSizing="dynamic">
|
||||
<mat-label>{{ 'rule-node-config.attributes-keys' | translate }}</mat-label>
|
||||
<mat-chip-grid formControlName="keys" #attributeChipList>
|
||||
<mat-chip-row
|
||||
*ngFor="let key of deleteAttributesConfigForm.get('keys').value;"
|
||||
(removed)="removeKey(key)">
|
||||
{{key}}
|
||||
<mat-icon matChipRemove>close</mat-icon>
|
||||
</mat-chip-row>
|
||||
<input matInput type="text"
|
||||
[matChipInputFor]="attributeChipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="addKey($event)"
|
||||
[matChipInputAddOnBlur]="true">
|
||||
</mat-chip-grid>
|
||||
<mat-error *ngIf="deleteAttributesConfigForm.get('keys').hasError('required')">{{ 'rule-node-config.attributes-keys-required' | translate }}</mat-error>
|
||||
<mat-hint translate>rule-node-config.general-pattern-hint</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<section class="tb-form-panel stroked">
|
||||
<mat-expansion-panel class="tb-settings">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title translate>rule-node-config.advanced-settings</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.send-attributes-deleted-notification-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesDeletedNotification">
|
||||
{{ 'rule-node-config.send-attributes-deleted-notification' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.notify-device-on-delete-hint' | translate }}"
|
||||
*ngIf="deleteAttributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice">
|
||||
{{ 'rule-node-config.notify-device' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</section>
|
||||
</section>
|
||||
@ -0,0 +1,91 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips';
|
||||
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models';
|
||||
import { AttributeScope, telemetryTypeTranslations } from '@shared/models/telemetry/telemetry.models';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-delete-attributes-config',
|
||||
templateUrl: './delete-attributes-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class DeleteAttributesConfigComponent extends RuleNodeConfigurationComponent {
|
||||
@ViewChild('attributeChipList') attributeChipList: MatChipGrid;
|
||||
|
||||
deleteAttributesConfigForm: UntypedFormGroup;
|
||||
attributeScopeMap = AttributeScope;
|
||||
attributeScopes = Object.keys(AttributeScope);
|
||||
telemetryTypeTranslationsMap = telemetryTypeTranslations;
|
||||
separatorKeysCodes = [ENTER, COMMA, SEMICOLON];
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.deleteAttributesConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.deleteAttributesConfigForm = this.fb.group({
|
||||
scope: [configuration ? configuration.scope : null, [Validators.required]],
|
||||
keys: [configuration ? configuration.keys : null, [Validators.required]],
|
||||
sendAttributesDeletedNotification: [configuration ? configuration.sendAttributesDeletedNotification : false, []],
|
||||
notifyDevice: [configuration ? configuration.notifyDevice : false, []]
|
||||
});
|
||||
|
||||
this.deleteAttributesConfigForm.get('scope').valueChanges.pipe(
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe((value) => {
|
||||
if (value !== AttributeScope.SHARED_SCOPE) {
|
||||
this.deleteAttributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeKey(key: string): void {
|
||||
const keys: string[] = this.deleteAttributesConfigForm.get('keys').value;
|
||||
const index = keys.indexOf(key);
|
||||
if (index >= 0) {
|
||||
keys.splice(index, 1);
|
||||
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true});
|
||||
}
|
||||
}
|
||||
|
||||
addKey(event: MatChipInputEvent): void {
|
||||
const input = event.input;
|
||||
let value = event.value;
|
||||
if ((value || '').trim()) {
|
||||
value = value.trim();
|
||||
let keys: string[] = this.deleteAttributesConfigForm.get('keys').value;
|
||||
if (!keys || keys.indexOf(value) === -1) {
|
||||
if (!keys) {
|
||||
keys = [];
|
||||
}
|
||||
keys.push(value);
|
||||
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true});
|
||||
}
|
||||
}
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="deleteRelationConfigForm" class="tb-form-panel no-padding no-border">
|
||||
<div class="tb-form-panel stroked no-padding-bottom">
|
||||
<div class="tb-form-panel-title" translate>rule-node-config.relation-parameters</div>
|
||||
<div class="flex flex-col">
|
||||
<mat-form-field class="mat-block" hideRequiredMarker>
|
||||
<mat-label translate>relation.direction</mat-label>
|
||||
<mat-select required matInput formControlName="direction">
|
||||
<mat-option *ngFor="let type of directionTypes" [value]="type">
|
||||
{{ directionTypeTranslations.get(type) | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<tb-relation-type-autocomplete
|
||||
required
|
||||
formControlName="relationType">
|
||||
</tb-relation-type-autocomplete>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<div tb-hint-tooltip-icon="{{ 'rule-node-config.delete-relation-with-specific-entity-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="deleteForSingleEntity">
|
||||
{{ 'rule-node-config.delete-relation-with-specific-entity' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div *ngIf="deleteRelationConfigForm.get('deleteForSingleEntity').value">
|
||||
<div class="flex flex-row gap-2.5">
|
||||
<tb-entity-type-select
|
||||
class="flex-1"
|
||||
showLabel
|
||||
required
|
||||
[allowedEntityTypes]="allowedEntityTypes"
|
||||
formControlName="entityType">
|
||||
</tb-entity-type-select>
|
||||
<mat-form-field *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT"
|
||||
class="mat-block flex-1">
|
||||
<mat-label>{{ entityTypeNamePatternTranslation.get(deleteRelationConfigForm.get('entityType').value) | translate }}</mat-label>
|
||||
<input required matInput formControlName="entityNamePattern">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<tb-example-hint *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT"
|
||||
[hintText]="'rule-node-config.kv-map-single-pattern-hint'"></tb-example-hint>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -0,0 +1,101 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { EntityType } from '@app/shared/models/entity-type.models';
|
||||
import { EntitySearchDirection } from '@app/shared/models/relation.models';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-delete-relation-config',
|
||||
templateUrl: './delete-relation-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class DeleteRelationConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
directionTypes = Object.keys(EntitySearchDirection);
|
||||
|
||||
directionTypeTranslations = new Map<EntitySearchDirection, string>(
|
||||
[
|
||||
[EntitySearchDirection.FROM, 'rule-node-config.del-relation-direction-from'],
|
||||
[EntitySearchDirection.TO, 'rule-node-config.del-relation-direction-to'],
|
||||
]
|
||||
);
|
||||
|
||||
entityTypeNamePatternTranslation = new Map<EntityType, string>(
|
||||
[
|
||||
[EntityType.DEVICE, 'rule-node-config.device-name-pattern'],
|
||||
[EntityType.ASSET, 'rule-node-config.asset-name-pattern'],
|
||||
[EntityType.ENTITY_VIEW, 'rule-node-config.entity-view-name-pattern'],
|
||||
[EntityType.CUSTOMER, 'rule-node-config.customer-title-pattern'],
|
||||
[EntityType.USER, 'rule-node-config.user-name-pattern'],
|
||||
[EntityType.DASHBOARD, 'rule-node-config.dashboard-name-pattern'],
|
||||
[EntityType.EDGE, 'rule-node-config.edge-name-pattern']
|
||||
]
|
||||
);
|
||||
|
||||
entityType = EntityType;
|
||||
|
||||
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT,
|
||||
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE];
|
||||
|
||||
deleteRelationConfigForm: UntypedFormGroup;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.deleteRelationConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.deleteRelationConfigForm = this.fb.group({
|
||||
deleteForSingleEntity: [configuration ? configuration.deleteForSingleEntity : false, []],
|
||||
direction: [configuration ? configuration.direction : null, [Validators.required]],
|
||||
entityType: [configuration ? configuration.entityType : null, []],
|
||||
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []],
|
||||
relationType: [configuration ? configuration.relationType : null, [Validators.required]]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['deleteForSingleEntity', 'entityType'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
const deleteForSingleEntity: boolean = this.deleteRelationConfigForm.get('deleteForSingleEntity').value;
|
||||
const entityType: EntityType = this.deleteRelationConfigForm.get('entityType').value;
|
||||
if (deleteForSingleEntity) {
|
||||
this.deleteRelationConfigForm.get('entityType').setValidators([Validators.required]);
|
||||
} else {
|
||||
this.deleteRelationConfigForm.get('entityType').setValidators([]);
|
||||
}
|
||||
if (deleteForSingleEntity && entityType && entityType !== EntityType.TENANT) {
|
||||
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]);
|
||||
} else {
|
||||
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([]);
|
||||
}
|
||||
this.deleteRelationConfigForm.get('entityType').updateValueAndValidity({emitEvent: false});
|
||||
this.deleteRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null;
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="deviceProfile" class="tb-form-panel stroked">
|
||||
<div class="tb-form-hint tb-primary-fill" translate>rule-node-config.device-profile-node-hint</div>
|
||||
<div tb-hint-tooltip-icon="{{'rule-node-config.persist-alarm-rules-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding slide-toggle">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="persistAlarmRulesState">
|
||||
{{ 'rule-node-config.persist-alarm-rules' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
<div tb-hint-tooltip-icon="{{'rule-node-config.fetch-alarm-rules-hint' | translate }}"
|
||||
class="tb-form-row no-border no-padding slide-toggle">
|
||||
<mat-slide-toggle class="mat-slide" formControlName="fetchAlarmRulesStateOnStart">
|
||||
{{ 'rule-node-config.fetch-alarm-rules' | translate }}
|
||||
</mat-slide-toggle>
|
||||
</div>
|
||||
</section>
|
||||
@ -0,0 +1,59 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-device-profile-config',
|
||||
templateUrl: './device-profile-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class DeviceProfileConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
deviceProfile: UntypedFormGroup;
|
||||
|
||||
constructor(private fb: UntypedFormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.deviceProfile;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.deviceProfile = this.fb.group({
|
||||
persistAlarmRulesState: [configuration ? configuration.persistAlarmRulesState : false],
|
||||
fetchAlarmRulesStateOnStart: [configuration ? configuration.fetchAlarmRulesStateOnStart : false]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['persistAlarmRulesState'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
if (this.deviceProfile.get('persistAlarmRulesState').value) {
|
||||
this.deviceProfile.get('fetchAlarmRulesStateOnStart').enable({emitEvent: false});
|
||||
} else {
|
||||
this.deviceProfile.get('fetchAlarmRulesStateOnStart').setValue(false, {emitEvent: false});
|
||||
this.deviceProfile.get('fetchAlarmRulesStateOnStart').disable({emitEvent: false});
|
||||
}
|
||||
this.deviceProfile.get('fetchAlarmRulesStateOnStart').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="deviceState">
|
||||
<mat-form-field class="mat-block" subscriptSizing="dynamic">
|
||||
<mat-label>{{ 'rule-node-config.select-device-connectivity-event' | translate }}</mat-label>
|
||||
<mat-select formControlName="event">
|
||||
<mat-option *ngFor="let eventOption of eventOptions" [value]="eventOption">
|
||||
{{ messageTypeNames.get(eventOption) }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
@ -0,0 +1,64 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { isDefinedAndNotNull } from '@core/public-api';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import {
|
||||
MessageType,
|
||||
messageTypeNames,
|
||||
RuleNodeConfiguration,
|
||||
RuleNodeConfigurationComponent
|
||||
} from '@shared/models/rule-node.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-device-state-config',
|
||||
templateUrl: './device-state-config.component.html',
|
||||
styleUrls: []
|
||||
})
|
||||
export class DeviceStateConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
deviceState: FormGroup;
|
||||
|
||||
public messageTypeNames = messageTypeNames;
|
||||
public eventOptions: MessageType[] = [
|
||||
MessageType.CONNECT_EVENT,
|
||||
MessageType.ACTIVITY_EVENT,
|
||||
MessageType.DISCONNECT_EVENT,
|
||||
MessageType.INACTIVITY_EVENT
|
||||
];
|
||||
|
||||
constructor(private fb: FormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): FormGroup {
|
||||
return this.deviceState;
|
||||
}
|
||||
|
||||
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
return {
|
||||
event: isDefinedAndNotNull(configuration?.event) ? configuration.event : MessageType.ACTIVITY_EVENT
|
||||
};
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.deviceState = this.fb.group({
|
||||
event: [configuration.event, [Validators.required]]
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2024 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.
|
||||
|
||||
-->
|
||||
<section [formGroup]="generatorConfigForm" class="tb-form-panel no-border no-padding">
|
||||
<div class="tb-form-panel no-padding-bottom stroked">
|
||||
<div class="tb-form-panel-title" translate>rule-node-config.generation-parameters</div>
|
||||
<div class="tb-form-row no-border no-padding tb-standard-fields column-xs">
|
||||
<mat-form-field class="flex">
|
||||
<mat-label translate>rule-node-config.message-count</mat-label>
|
||||
<input required type="number" min="0" step="1" matInput formControlName="msgCount">
|
||||
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('required')">
|
||||
{{ 'rule-node-config.message-count-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('min')">
|
||||
{{ 'rule-node-config.min-message-count-message' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex">
|
||||
<mat-label translate>rule-node-config.generation-frequency-seconds</mat-label>
|
||||
<input required type="number" min="1" step="1" matInput formControlName="periodInSeconds">
|
||||
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('required')">
|
||||
{{ 'rule-node-config.generation-frequency-required' | translate }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('min')">
|
||||
{{ 'rule-node-config.min-generation-frequency-message' | translate }}
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tb-form-panel no-padding-bottom stroked">
|
||||
<div class="tb-form-panel-title" translate>rule-node-config.originator</div>
|
||||
<tb-entity-select class="flex-1"
|
||||
required="true"
|
||||
useAliasEntityTypes="true"
|
||||
[allowedEntityTypes]="allowedEntityTypes"
|
||||
[additionEntityTypes]="additionEntityTypes"
|
||||
formControlName="originator">
|
||||
</tb-entity-select>
|
||||
</div>
|
||||
<div class="tb-form-panel stroked">
|
||||
<mat-expansion-panel class="tb-settings" expanded>
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title translate>rule-node-config.generator-function</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.JS"
|
||||
#jsFuncComponent
|
||||
formControlName="jsScript"
|
||||
functionName="Generate"
|
||||
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']"
|
||||
helpId="rulenode/generator_node_script_fn"
|
||||
noValidate="true">
|
||||
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill" *ngIf="tbelEnabled">
|
||||
<tb-toggle-option [value]="scriptLanguage.TBEL">
|
||||
{{ 'rule-node-config.script-lang-tbel' | translate }}
|
||||
</tb-toggle-option>
|
||||
<tb-toggle-option [value]="scriptLanguage.JS">
|
||||
{{ 'rule-node-config.script-lang-js' | translate }}
|
||||
</tb-toggle-option>
|
||||
</tb-toggle-select>
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.TBEL"
|
||||
#tbelFuncComponent
|
||||
formControlName="tbelScript"
|
||||
functionName="Generate"
|
||||
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']"
|
||||
[disableUndefinedCheck]="true"
|
||||
[scriptLanguage]="scriptLanguage.TBEL"
|
||||
helpId="rulenode/tbel/generator_node_script_fn"
|
||||
noValidate="true">
|
||||
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill">
|
||||
<tb-toggle-option [value]="scriptLanguage.TBEL">
|
||||
{{ 'rule-node-config.script-lang-tbel' | translate }}
|
||||
</tb-toggle-option>
|
||||
<tb-toggle-option [value]="scriptLanguage.JS">
|
||||
{{ 'rule-node-config.script-lang-js' | translate }}
|
||||
</tb-toggle-option>
|
||||
</tb-toggle-select>
|
||||
<button toolbarSuffixButton
|
||||
mat-icon-button
|
||||
matTooltip="{{ testScriptLabel | translate }}"
|
||||
matTooltipPosition="above"
|
||||
class="tb-mat-32"
|
||||
(click)="testScript()">
|
||||
<mat-icon class="material-icons" color="primary">bug_report</mat-icon>
|
||||
</button>
|
||||
</tb-js-func>
|
||||
<div class="flex flex-row" style="padding-bottom: 16px;">
|
||||
<button mat-button mat-raised-button color="primary" (click)="testScript()">
|
||||
{{ testScriptLabel | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</div>
|
||||
</section>
|
||||
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright © 2016-2024 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.
|
||||
*/
|
||||
:host {
|
||||
::ng-deep {
|
||||
.mat-button-toggle-group {
|
||||
min-width: 120px;
|
||||
height: 24px !important;
|
||||
.mat-button-toggle {
|
||||
font-size: 0;
|
||||
.mat-button-toggle-button {
|
||||
height: 20px!important;
|
||||
line-height: 20px !important;
|
||||
border: none !important;
|
||||
.mat-button-toggle-label-content {
|
||||
font-size: 14px !important;
|
||||
line-height: 20px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tb-entity-select {
|
||||
@media screen and (min-width: 599px) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
}
|
||||
tb-entity-type-select {
|
||||
flex: 1;
|
||||
}
|
||||
tb-entity-autocomplete {
|
||||
flex: 1;
|
||||
mat-form-field {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Component, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { getCurrentAuthState, isDefinedAndNotNull, NodeScriptTestService } from '@core/public-api';
|
||||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
RuleNodeConfiguration,
|
||||
RuleNodeConfigurationComponent,
|
||||
ScriptLanguage
|
||||
} from '@app/shared/models/rule-node.models';
|
||||
import type { JsFuncComponent } from '@app/shared/components/js-func.component';
|
||||
import { EntityType } from '@app/shared/models/entity-type.models';
|
||||
import { DebugRuleNodeEventBody } from '@shared/models/event.models';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-action-node-generator-config',
|
||||
templateUrl: './generator-config.component.html',
|
||||
styleUrls: ['generator-config.component.scss']
|
||||
})
|
||||
export class GeneratorConfigComponent extends RuleNodeConfigurationComponent {
|
||||
|
||||
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent;
|
||||
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent;
|
||||
|
||||
generatorConfigForm: UntypedFormGroup;
|
||||
|
||||
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled;
|
||||
|
||||
scriptLanguage = ScriptLanguage;
|
||||
|
||||
changeScript: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
allowedEntityTypes = [
|
||||
EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.CUSTOMER,
|
||||
EntityType.USER, EntityType.DASHBOARD
|
||||
];
|
||||
|
||||
additionEntityTypes = {
|
||||
TENANT: this.translate.instant('rule-node-config.current-tenant'),
|
||||
RULE_NODE: this.translate.instant('rule-node-config.current-rule-node')
|
||||
};
|
||||
|
||||
readonly hasScript = true;
|
||||
|
||||
readonly testScriptLabel = 'rule-node-config.test-generator-function';
|
||||
|
||||
constructor(private fb: UntypedFormBuilder,
|
||||
private nodeScriptTestService: NodeScriptTestService,
|
||||
private translate: TranslateService) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected configForm(): UntypedFormGroup {
|
||||
return this.generatorConfigForm;
|
||||
}
|
||||
|
||||
protected onConfigurationSet(configuration: RuleNodeConfiguration) {
|
||||
this.generatorConfigForm = this.fb.group({
|
||||
msgCount: [configuration ? configuration.msgCount : null, [Validators.required, Validators.min(0)]],
|
||||
periodInSeconds: [configuration ? configuration.periodInSeconds : null, [Validators.required, Validators.min(1)]],
|
||||
originator: [configuration ? configuration.originator : {id: null, entityType: EntityType.RULE_NODE}, []],
|
||||
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]],
|
||||
jsScript: [configuration ? configuration.jsScript : null, []],
|
||||
tbelScript: [configuration ? configuration.tbelScript : null, []]
|
||||
});
|
||||
}
|
||||
|
||||
protected validatorTriggers(): string[] {
|
||||
return ['scriptLang'];
|
||||
}
|
||||
|
||||
protected updateValidators(emitEvent: boolean) {
|
||||
let scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) {
|
||||
scriptLang = ScriptLanguage.JS;
|
||||
this.generatorConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false});
|
||||
setTimeout(() => {this.generatorConfigForm.updateValueAndValidity({emitEvent: true});});
|
||||
}
|
||||
this.generatorConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []);
|
||||
this.generatorConfigForm.get('jsScript').updateValueAndValidity({emitEvent});
|
||||
this.generatorConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []);
|
||||
this.generatorConfigForm.get('tbelScript').updateValueAndValidity({emitEvent});
|
||||
}
|
||||
|
||||
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
return {
|
||||
msgCount: isDefinedAndNotNull(configuration?.msgCount) ? configuration?.msgCount : 0,
|
||||
periodInSeconds: isDefinedAndNotNull(configuration?.periodInSeconds) ? configuration?.periodInSeconds : 1,
|
||||
originator: {
|
||||
id: isDefinedAndNotNull(configuration?.originatorId) ? configuration?.originatorId : null,
|
||||
entityType: isDefinedAndNotNull(configuration?.originatorType) ? configuration?.originatorType : EntityType.RULE_NODE
|
||||
},
|
||||
scriptLang: isDefinedAndNotNull(configuration?.scriptLang) ? configuration?.scriptLang : ScriptLanguage.JS,
|
||||
tbelScript: isDefinedAndNotNull(configuration?.tbelScript) ? configuration?.tbelScript : null,
|
||||
jsScript: isDefinedAndNotNull(configuration?.jsScript) ? configuration?.jsScript : null,
|
||||
};
|
||||
}
|
||||
|
||||
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration {
|
||||
if (configuration.originator) {
|
||||
configuration.originatorId = configuration.originator.id;
|
||||
configuration.originatorType = configuration.originator.entityType;
|
||||
} else {
|
||||
configuration.originatorId = null;
|
||||
configuration.originatorType = null;
|
||||
}
|
||||
delete configuration.originator;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
testScript(debugEventBody?: DebugRuleNodeEventBody) {
|
||||
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value;
|
||||
const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript';
|
||||
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/generator_node_script_fn' : 'rulenode/tbel/generator_node_script_fn';
|
||||
const script: string = this.generatorConfigForm.get(scriptField).value;
|
||||
this.nodeScriptTestService.testNodeScript(
|
||||
script,
|
||||
'generate',
|
||||
this.translate.instant('rule-node-config.generator'),
|
||||
'Generate',
|
||||
['prevMsg', 'prevMetadata', 'prevMsgType'],
|
||||
this.ruleNodeId,
|
||||
helpId,
|
||||
scriptLang,
|
||||
debugEventBody
|
||||
).subscribe((theScript) => {
|
||||
if (theScript) {
|
||||
this.generatorConfigForm.get(scriptField).setValue(theScript);
|
||||
this.changeScript.emit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected onValidate() {
|
||||
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value;
|
||||
if (scriptLang === ScriptLanguage.JS) {
|
||||
this.jsFuncComponent.validateOnSubmit();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user